Home | History | Annotate | Download | only in util
      1 /* Licensed to the Apache Software Foundation (ASF) under one or more
      2  * contributor license agreements.  See the NOTICE file distributed with
      3  * this work for additional information regarding copyright ownership.
      4  * The ASF licenses this file to You under the Apache License, Version 2.0
      5  * (the "License"); you may not use this file except in compliance with
      6  * the License.  You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package java.util;
     17 
     18 import java.io.BufferedWriter;
     19 import java.io.Closeable;
     20 import java.io.File;
     21 import java.io.FileNotFoundException;
     22 import java.io.FileOutputStream;
     23 import java.io.Flushable;
     24 import java.io.IOException;
     25 import java.io.OutputStream;
     26 import java.io.OutputStreamWriter;
     27 import java.io.PrintStream;
     28 import java.io.UnsupportedEncodingException;
     29 import java.math.BigDecimal;
     30 import java.math.BigInteger;
     31 import java.math.MathContext;
     32 import java.nio.charset.Charset;
     33 import libcore.icu.LocaleData;
     34 import libcore.icu.NativeDecimalFormat;
     35 import libcore.io.IoUtils;
     36 
     37 /**
     38  * Formats arguments according to a format string (like {@code printf} in C).
     39  * <p>
     40  * It's relatively rare to use a {@code Formatter} directly. A variety of classes offer convenience
     41  * methods for accessing formatter functionality.
     42  * Of these, {@link String#format} is generally the most useful.
     43  * {@link java.io.PrintStream} and {@link java.io.PrintWriter} both offer
     44  * {@code format} and {@code printf} methods.
     45  * <p>
     46  * <i>Format strings</i> consist of plain text interspersed with format specifiers, such
     47  * as {@code "name: %s weight: %03dkg\n"}. Being a Java string, the usual Java string literal
     48  * backslash escapes are of course available.
     49  * <p>
     50  * <i>Format specifiers</i> (such as {@code "%s"} or {@code "%03d"} in the example) start with a
     51  * {@code %} and describe how to format their corresponding argument. It includes an optional
     52  * argument index, optional flags, an optional width, an optional precision, and a mandatory
     53  * conversion type.
     54  * In the example, {@code "%s"} has no flags, no width, and no precision, while
     55  * {@code "%03d"} has the flag {@code 0}, the width 3, and no precision.
     56  * <p>
     57  * Not all combinations of argument index, flags, width, precision, and conversion type
     58  * are valid.
     59  * <p>
     60  * <i>Argument index</i>. Normally, each format specifier consumes the next argument to
     61  * {@code format}.
     62  * For convenient localization, it's possible to reorder arguments so that they appear in a
     63  * different order in the output than the order in which they were supplied.
     64  * For example, {@code "%4$s"} formats the fourth argument ({@code 4$}) as a string ({@code s}).
     65  * It's also possible to reuse an argument with {@code <}. For example,
     66  * {@code format("%o %&lt;d %&lt;x", 64)} results in {@code "100 64 40"}.
     67  * <p>
     68  * <i>Flags</i>. The available flags are:
     69  * <p>
     70  * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
     71  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor"> <TD COLSPAN=4> <B>Flags</B> </TD> </tr>
     72  * <tr>
     73  * <td width="5%">{@code ,}</td>
     74  * <td width="25%">Use grouping separators for large numbers. (Decimal only.)</td>
     75  * <td width="30%">{@code format("%,d", 1024);}</td>
     76  * <td width="30%">{@code 1,234}</td>
     77  * </tr>
     78  * <tr>
     79  * <td width="5%">{@code +}</td>
     80  * <td width="25%">Always show sign. (Decimal only.)</td>
     81  * <td width="30%">{@code format("%+d, %+4d", 5, 5);}</td>
     82  * <td width="30%"><pre>+5,   +5</pre></td>
     83  * </tr>
     84  * <tr>
     85  * <td width="5%">{@code  }</td>
     86  * <td width="25%">A space indicates that non-negative numbers
     87  * should have a leading space. (Decimal only.)</td>
     88  * <td width="30%">{@code format("x% d% 5d", 4, 4);}</td>
     89  * <td width="30%"><pre>x 4    4</pre></td>
     90  * </tr>
     91  * <tr>
     92  * <td width="5%">{@code (}</td>
     93  * <td width="25%">Put parentheses around negative numbers. (Decimal only.)</td>
     94  * <td width="30%">{@code format("%(d, %(d, %(6d", 12, -12, -12);}</td>
     95  * <td width="30%"><pre>12, (12),   (12)</pre></td>
     96  * </tr>
     97  * <tr>
     98  * <td width="5%">{@code -}</td>
     99  * <td width="25%">Left-justify. (Requires width.)</td>
    100  * <td width="30%">{@code format("%-6dx", 5);}<br/>{@code format("%-3C, %3C", 'd', 0x65);}</td>
    101  * <td width="30%"><pre>5      x</pre><br/><pre>D  ,   E</pre></td>
    102  * </tr>
    103  * <tr>
    104  * <td width="5%">{@code 0}</td>
    105  * <td width="25%">Pad the number with leading zeros. (Requires width.)</td>
    106  * <td width="30%">{@code format("%07d, %03d", 4, 5555);}</td>
    107  * <td width="30%">{@code 0000004, 5555}</td>
    108  * </tr>
    109  * <tr>
    110  * <td width="5%">{@code #}</td>
    111  * <td width="25%">Alternate form. (Octal and hex only.) </td>
    112  * <td width="30%">{@code format("%o %#o", 010, 010);}<br/>{@code format("%x %#x", 0x12, 0x12);}</td>
    113  * <td width="30%">{@code 10 010}<br/>{@code 12 0x12}</td>
    114  * </tr>
    115  * </table>
    116  * <p>
    117  * <i>Width</i>. The width is a decimal integer specifying the minimum number of characters to be
    118  * used to represent the argument. If the result would otherwise be shorter than the width, padding
    119  * will be added (the exact details of which depend on the flags). Note that you can't use width to
    120  * truncate a field, only to make it wider: see precision for control over the maximum width.
    121  * <p>
    122  * <i>Precision</i>. The precision is a {@code .} followed by a decimal integer, giving the minimum
    123  * number of digits for {@code d}, {@code o}, {@code x}, or {@code X}; the minimum number of digits
    124  * after the decimal point for {@code a}, {@code A}, {@code e}, {@code E}, {@code f}, or {@code F};
    125  * the maximum number of significant digits for {@code g} or {@code G}; or the maximum number of
    126  * characters for {@code s} or {@code S}.
    127  * <p>
    128  * <i>Conversion type</i>. One or two characters describing how to interpret the argument. Most
    129  * conversions are a single character, but date/time conversions all start with {@code t} and
    130  * have a single extra character describing the desired output.
    131  * <p>
    132  * Many conversion types have a corresponding uppercase variant that converts its result to
    133  * uppercase using the rules of the relevant locale (either the default or the locale set for
    134  * this formatter).
    135  * <p>
    136  * This table shows the available single-character (non-date/time) conversion types:
    137  * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
    138  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    139  * <TD COLSPAN=4>
    140  * <B>String conversions</B>
    141  * <br>
    142  * All types are acceptable arguments. Values of type {@link Formattable} have their
    143  * {@code formatTo} method invoked; all other types use {@code toString}.
    144  * </TD>
    145  * </tr>
    146  * <tr>
    147  * <td width="5%">{@code s}</td>
    148  * <td width="25%">String.</td>
    149  * <td width="30%">{@code format("%s %s", "hello", "Hello");}</td>
    150  * <td width="30%">{@code hello Hello}</td>
    151  * </tr>
    152  * <tr>
    153  * <td width="5%">{@code S}</td>
    154  * <td width="25%">Uppercase string.</td>
    155  * <td width="30%">{@code format("%S %S", "hello", "Hello");}</td>
    156  * <td width="30%">{@code HELLO HELLO}</td>
    157  * </tr>
    158  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    159  * <TD COLSPAN=4>
    160  * <B>Character conversions</B>
    161  * <br>
    162  * Byte, Character, Short, and Integer (and primitives that box to those types) are all acceptable
    163  * as character arguments. Any other type is an error.
    164  * </TD>
    165  * </tr>
    166  * <tr>
    167  * <td width="5%">{@code c}</td>
    168  * <td width="25%">Character.</td>
    169  * <td width="30%">{@code format("%c %c", 'd', 'E');}</td>
    170  * <td width="30%">{@code d E}</td>
    171  * </tr>
    172  * <tr>
    173  * <td width="5%">{@code C}</td>
    174  * <td width="25%">Uppercase character.</td>
    175  * <td width="30%">{@code format("%C %C", 'd', 'E');}</td>
    176  * <td width="30%">{@code D E}</td>
    177  * </tr>
    178  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    179  * <TD COLSPAN=4>
    180  * <B>Integer conversions</B>
    181  * <br>
    182  * Byte, Short, Integer, Long, and BigInteger (and primitives that box to those types) are all
    183  * acceptable as integer arguments. Any other type is an error.
    184  * </TD>
    185  * </tr>
    186  * <tr>
    187  * <td width="5%">{@code d}</td>
    188  * <td width="25%">Decimal.</td>
    189  * <td width="30%">{@code format("%d", 26);}</td>
    190  * <td width="30%">{@code 26}</td>
    191  * </tr>
    192  * <tr>
    193  * <td width="5%">{@code o}</td>
    194  * <td width="25%">Octal.</td>
    195  * <td width="30%">{@code format("%o", 032);}</td>
    196  * <td width="30%">{@code 32}</td>
    197  * </tr>
    198  * <tr>
    199  * <td width="5%">{@code x}, {@code X}</td>
    200  * <td width="25%">Hexadecimal.</td>
    201  * <td width="30%">{@code format("%x %X", 0x1a, 0x1a);}</td>
    202  * <td width="30%">{@code 1a 1A}</td>
    203  * </tr>
    204  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    205  * <TD COLSPAN=4><B>Floating-point conversions</B>
    206  * <br>
    207  * Float, Double, and BigDecimal (and primitives that box to those types) are all acceptable as
    208  * floating-point arguments. Any other type is an error.
    209  * </TD>
    210  * </tr>
    211  * <tr>
    212  * <td width="5%">{@code f}</td>
    213  * <td width="25%">Decimal floating point.</td>
    214  * <td width="30%"><pre>
    215 format("%f", 123.456f);
    216 format("%.1f", 123.456f);
    217 format("%1.5f", 123.456f);
    218 format("%10f", 123.456f);
    219 format("%6.0f", 123.456f);</td>
    220  * <td width="30%" valign="top"><pre>
    221 123.456001
    222 123.5
    223 123.45600
    224 123.456001
    225 &nbsp;&nbsp;&nbsp;123</pre></td>
    226  * </tr>
    227  * <tr>
    228  * <td width="5%">{@code e}, {@code E}</td>
    229  * <td width="25%">Engineering/exponential floating point.</td>
    230  * <td width="30%"><pre>
    231 format("%e", 123.456f);
    232 format("%.1e", 123.456f);
    233 format("%1.5E", 123.456f);
    234 format("%10E", 123.456f);
    235 format("%6.0E", 123.456f);</td>
    236  * <td width="30%" valign="top"><pre>
    237 1.234560e+02
    238 1.2e+02
    239 1.23456E+02
    240 1.234560E+02
    241 &nbsp;1E+02</pre></td>
    242  * </tr>
    243  * <tr>
    244  * <td width="5%" valign="top">{@code g}, {@code G}</td>
    245  * <td width="25%" valign="top">Decimal or engineering, depending on the magnitude of the value.</td>
    246  * <td width="30%" valign="top">{@code format("%g %g", 0.123, 0.0000123);}</td>
    247  * <td width="30%" valign="top">{@code 0.123000 1.23000e-05}</td>
    248  * </tr>
    249  * <tr>
    250  * <td width="5%">{@code a}, {@code A}</td>
    251  * <td width="25%">Hexadecimal floating point.</td>
    252  * <td width="30%">{@code format("%a", 123.456f);}</td>
    253  * <td width="30%">{@code 0x1.edd2f2p6}</td>
    254  * </tr>
    255  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    256  * <TD COLSPAN=4>
    257  * <B>Boolean conversion</B>
    258  * <br>
    259  * Accepts Boolean values. {@code null} is considered false, and instances of all other
    260  * types are considered true.
    261  * </TD>
    262  * </tr>
    263  * <tr>
    264  * <td width="5%">{@code b}, {@code B}</td>
    265  * <td width="25%">Boolean.</td>
    266  * <td width="30%">{@code format("%b %b", true, false);}<br>{@code format("%B %B", true, false);}<br>{@code format("%b", null);}<br>{@code format("%b", "hello");}</td>
    267  * <td width="30%">{@code true false}<br>{@code TRUE FALSE}<br>{@code false}<br>{@code true}</td>
    268  * </tr>
    269  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    270  * <TD COLSPAN=4>
    271  * <B>Hash code conversion</B>
    272  * <br>
    273  * Invokes {@code hashCode} on its argument, which may be of any type.
    274  * </TD>
    275  * </tr>
    276  * <tr>
    277  * <td width="5%">{@code h}, {@code H}</td>
    278  * <td width="25%">Hexadecimal hash code.</td>
    279  * <td width="30%">{@code format("%h", this);}<br>{@code format("%H", this);}<br>{@code format("%h", null);}</td>
    280  * <td width="30%">{@code 190d11}<br>{@code 190D11}<br>{@code null}</td>
    281  * </tr>
    282  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    283  * <TD COLSPAN=4>
    284  * <B>Zero-argument conversions</B></TD>
    285  * </tr>
    286  * <tr>
    287  * <td width="5%">{@code %}</td>
    288  * <td width="25%">A literal % character.</td>
    289  * <td width="30%">{@code format("%d%%", 50);}</td>
    290  * <td width="30%">{@code 50%}</td>
    291  * </tr>
    292  * <tr>
    293  * <td width="5%">{@code n}</td>
    294  * <td width="25%">Newline. (The value of the "line.separator" system property}.)</td>
    295  * <td width="30%">{@code format("first%nsecond");}</td>
    296  * <td width="30%">{@code first\nsecond}</td>
    297  * </tr>
    298  * </table>
    299  * <p>
    300  * It's also possible to format dates and times with {@code Formatter}, though you should
    301  * use {@link java.text.SimpleDateFormat} (probably via the factory methods in
    302  * {@link java.text.DateFormat}) instead.
    303  * The facilities offered by {@code Formatter} are low-level and place the burden of localization
    304  * on the developer. Using {@link java.text.DateFormat#getDateInstance},
    305  * {@link java.text.DateFormat#getTimeInstance}, and
    306  * {@link java.text.DateFormat#getDateTimeInstance} is preferable for dates and times that will be
    307  * presented to a human. Those methods will select the best format strings for the user's locale.
    308  * <p>
    309  * The best non-localized form is <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>,
    310  * which you can get with {@code "%tF"} (2010-01-22), {@code "%tF %tR"} (2010-01-22 13:39),
    311  * {@code "%tF %tT"} (2010-01-22 13:39:15), or {@code "%tF %tT%z"} (2010-01-22 13:39:15-0800).
    312  * <p>
    313  * This table shows the date/time conversions, but you should use {@link java.text.SimpleDateFormat}
    314  * instead:
    315  * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
    316  * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    317  * <TD COLSPAN=4><B>Date/time conversions</B>
    318  * <br>
    319  * Calendar, Date, and Long (representing milliseconds past the epoch) are all acceptable
    320  * as date/time arguments. Any other type is an error. The epoch is 1970-01-01 00:00:00 UTC.
    321  * <font color="red">Use {@link java.text.SimpleDateFormat} instead.</font>
    322  * </TD>
    323  * </tr>
    324  * <tr>
    325  * <td width="5%">{@code ta}</td>
    326  * <td width="25%">Localized weekday name (abbreviated).</td>
    327  * <td width="30%">{@code format("%ta", cal, cal);}</td>
    328  * <td width="30%">{@code Tue}</td>
    329  * </tr>
    330  * <tr>
    331  * <td width="5%">{@code tA}</td>
    332  * <td width="25%">Localized weekday name (full).</td>
    333  * <td width="30%">{@code format("%tA", cal, cal);}</td>
    334  * <td width="30%">{@code Tuesday}</td>
    335  * </tr>
    336  * <tr>
    337  * <td width="5%">{@code tb}</td>
    338  * <td width="25%">Localized month name (abbreviated).</td>
    339  * <td width="30%">{@code format("%tb", cal);}</td>
    340  * <td width="30%">{@code Apr}</td>
    341  * </tr>
    342  * <tr>
    343  * <td width="5%">{@code tB}</td>
    344  * <td width="25%">Localized month name (full).</td>
    345  * <td width="30%">{@code format("%tB", cal);}</td>
    346  * <td width="30%">{@code April}</td>
    347  * </tr>
    348  * <tr>
    349  * <td width="5%">{@code tc}</td>
    350  * <td width="25%">C library <i>asctime(3)</i>-like output. Do not use.</td>
    351  * <td width="30%">{@code format("%tc", cal);}</td>
    352  * <td width="30%">{@code Tue Apr 01 16:19:17 CEST 2008}</td>
    353  * </tr>
    354  * <tr>
    355  * <td width="5%">{@code tC}</td>
    356  * <td width="25%">2-digit century.</td>
    357  * <td width="30%">{@code format("%tC", cal);}</td>
    358  * <td width="30%">{@code 20}</td>
    359  * </tr>
    360  * <tr>
    361  * <td width="5%">{@code td}</td>
    362  * <td width="25%">2-digit day of month (01-31).</td>
    363  * <td width="30%">{@code format("%td", cal);}</td>
    364  * <td width="30%">{@code 01}</td>
    365  * </tr>
    366  * <tr>
    367  * <td width="5%">{@code tD}</td>
    368  * <td width="25%">Ambiguous US date format (MM/DD/YY). Do not use.</td>
    369  * <td width="30%">{@code format("%tD", cal);}</td>
    370  * <td width="30%">{@code 04/01/08}</td>
    371  * </tr>
    372  * <tr>
    373  * <td width="5%">{@code te}</td>
    374  * <td width="25%">Day of month (1-31).</td>
    375  * <td width="30%">{@code format("%te", cal);}</td>
    376  * <td width="30%">{@code 1}</td>
    377  * </tr>
    378  * <tr>
    379  * <td width="5%">{@code tF}</td>
    380  * <td width="25%">Full date in ISO 8601 format (YYYY-MM-DD).</td>
    381  * <td width="30%">{@code format("%tF", cal);}</td>
    382  * <td width="30%">{@code 2008-04-01}</td>
    383  * </tr>
    384  * <tr>
    385  * <td width="5%">{@code th}</td>
    386  * <td width="25%">Synonym for {@code %tb}.</td>
    387  * <td width="30%"></td>
    388  * <td width="30%"></td>
    389  * </tr>
    390  * <tr>
    391  * <td width="5%">{@code tH}</td>
    392  * <td width="25%">2-digit 24-hour hour of day (00-23).</td>
    393  * <td width="30%">{@code format("%tH", cal);}</td>
    394  * <td width="30%">{@code 16}</td>
    395  * </tr>
    396  * <tr>
    397  * <td width="5%">{@code tI}</td>
    398  * <td width="25%">2-digit 12-hour hour of day (01-12).</td>
    399  * <td width="30%">{@code format("%tI", cal);}</td>
    400  * <td width="30%">{@code 04}</td>
    401  * </tr>
    402  * <tr>
    403  * <td width="5%">{@code tj}</td>
    404  * <td width="25%">3-digit day of year (001-366).</td>
    405  * <td width="30%">{@code format("%tj", cal);}</td>
    406  * <td width="30%">{@code 092}</td>
    407  * </tr>
    408  * <tr>
    409  * <td width="5%">{@code tk}</td>
    410  * <td width="25%">24-hour hour of day (0-23).</td>
    411  * <td width="30%">{@code format("%tk", cal);}</td>
    412  * <td width="30%">{@code 16}</td>
    413  * </tr>
    414  * <tr>
    415  * <td width="5%">{@code tl}</td>
    416  * <td width="25%">12-hour hour of day (1-12).</td>
    417  * <td width="30%">{@code format("%tl", cal);}</td>
    418  * <td width="30%">{@code 4}</td>
    419  * </tr>
    420  * <tr>
    421  * <td width="5%">{@code tL}</td>
    422  * <td width="25%">Milliseconds.</td>
    423  * <td width="30%">{@code format("%tL", cal);}</td>
    424  * <td width="30%">{@code 359}</td>
    425  * </tr>
    426  * <tr>
    427  * <td width="5%">{@code tm}</td>
    428  * <td width="25%">2-digit month of year (01-12).</td>
    429  * <td width="30%">{@code format("%tm", cal);}</td>
    430  * <td width="30%">{@code 04}</td>
    431  * </tr>
    432  * <tr>
    433  * <td width="5%">{@code tM}</td>
    434  * <td width="25%">2-digit minute.</td>
    435  * <td width="30%">{@code format("%tM", cal);}</td>
    436  * <td width="30%">{@code 08}</td>
    437  * </tr>
    438  * <tr>
    439  * <td width="5%">{@code tN}</td>
    440  * <td width="25%">Nanoseconds.</td>
    441  * <td width="30%">{@code format("%tN", cal);}</td>
    442  * <td width="30%">{@code 359000000}</td>
    443  * </tr>
    444  * <tr>
    445  * <td width="5%">{@code tp}</td>
    446  * <td width="25%">a.m. or p.m.</td>
    447  * <td width="30%">{@code format("%tp %Tp", cal, cal);}</td>
    448  * <td width="30%">{@code pm PM}</td>
    449  * </tr>
    450  * <tr>
    451  * <td width="5%">{@code tQ}</td>
    452  * <td width="25%">Milliseconds since the epoch.</td>
    453  * <td width="30%">{@code format("%tQ", cal);}</td>
    454  * <td width="30%">{@code 1207059412656}</td>
    455  * </tr>
    456  * <tr>
    457  * <td width="5%">{@code tr}</td>
    458  * <td width="25%">Full 12-hour time ({@code %tI:%tM:%tS %Tp}).</td>
    459  * <td width="30%">{@code format("%tr", cal);}</td>
    460  * <td width="30%">{@code 04:15:32 PM}</td>
    461  * </tr>
    462  * <tr>
    463  * <td width="5%">{@code tR}</td>
    464  * <td width="25%">Short 24-hour time ({@code %tH:%tM}).</td>
    465  * <td width="30%">{@code format("%tR", cal);}</td>
    466  * <td width="30%">{@code 16:15}</td>
    467  * </tr>
    468  * <tr>
    469  * <td width="5%">{@code ts}</td>
    470  * <td width="25%">Seconds since the epoch.</td>
    471  * <td width="30%">{@code format("%ts", cal);}</td>
    472  * <td width="30%">{@code 1207059412}</td>
    473  * </tr>
    474  * <tr>
    475  * <td width="5%">{@code tS}</td>
    476  * <td width="25%">2-digit seconds (00-60).</td>
    477  * <td width="30%">{@code format("%tS", cal);}</td>
    478  * <td width="30%">{@code 17}</td>
    479  * </tr>
    480  * <tr>
    481  * <td width="5%">{@code tT}</td>
    482  * <td width="25%">Full 24-hour time ({@code %tH:%tM:%tS}).</td>
    483  * <td width="30%">{@code format("%tT", cal);}</td>
    484  * <td width="30%">{@code 16:15:32}</td>
    485  * </tr>
    486  * <tr>
    487  * <td width="5%">{@code ty}</td>
    488  * <td width="25%">2-digit year (00-99).</td>
    489  * <td width="30%">{@code format("%ty", cal);}</td>
    490  * <td width="30%">{@code 08}</td>
    491  * </tr>
    492  * <tr>
    493  * <td width="5%">{@code tY}</td>
    494  * <td width="25%">4-digit year.</td>
    495  * <td width="30%">{@code format("%tY", cal);}</td>
    496  * <td width="30%">{@code 2008}</td>
    497  * </tr>
    498  * <tr>
    499  * <td width="5%">{@code tz}</td>
    500  * <td width="25%">Time zone GMT offset.</td>
    501  * <td width="30%">{@code format("%tz", cal);}</td>
    502  * <td width="30%">{@code +0100}</td>
    503  * </tr>
    504  * <tr>
    505  * <td width="5%">{@code tZ}</td>
    506  * <td width="25%">Localized time zone abbreviation.</td>
    507  * <td width="30%">{@code format("%tZ", cal);}</td>
    508  * <td width="30%">{@code CEST}</td>
    509  * </tr>
    510  * </table>
    511  * <p>
    512  * As with the other conversions, date/time conversion has an uppercase format. Replacing
    513  * {@code %t} with {@code %T} will uppercase the field according to the rules of the formatter's
    514  * locale.
    515  * <p><i>Number localization</i>. Some conversions use localized decimal digits rather than the
    516  * usual ASCII digits. So formatting {@code 123} with {@code %d} will give 123 in English locales
    517  * but &#x0661;&#x0662;&#x0663; in appropriate Arabic locales, for example. This number localization
    518  * occurs for the decimal integer conversion {@code %d}, the floating point conversions {@code %e},
    519  * {@code %f}, and {@code %g}, and all date/time {@code %t} or {@code %T} conversions, but no other
    520  * conversions.
    521  * <p><i>Thread safety</i>. Formatter is not thread-safe.
    522  *
    523  * @since 1.5
    524  * @see java.text.DateFormat
    525  * @see Formattable
    526  * @see java.text.SimpleDateFormat
    527  */
    528 public final class Formatter implements Closeable, Flushable {
    529     private static final char[] ZEROS = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', '0' };
    530 
    531     /**
    532      * The enumeration giving the available styles for formatting very large
    533      * decimal numbers.
    534      */
    535     public enum BigDecimalLayoutForm {
    536         /**
    537          * Use scientific style for BigDecimals.
    538          */
    539         SCIENTIFIC,
    540         /**
    541          * Use normal decimal/float style for BigDecimals.
    542          */
    543         DECIMAL_FLOAT
    544     }
    545 
    546     // User-settable parameters.
    547     private Appendable out;
    548     private Locale locale;
    549 
    550     // Implementation details.
    551     private Object arg;
    552     private boolean closed = false;
    553     private FormatToken formatToken;
    554     private IOException lastIOException;
    555     private LocaleData localeData;
    556 
    557     private static class CachedDecimalFormat {
    558         public NativeDecimalFormat decimalFormat;
    559         public LocaleData currentLocaleData;
    560         public String currentPattern;
    561 
    562         public CachedDecimalFormat() {
    563         }
    564 
    565         public NativeDecimalFormat update(LocaleData localeData, String pattern) {
    566             if (decimalFormat == null) {
    567                 currentPattern = pattern;
    568                 currentLocaleData = localeData;
    569                 decimalFormat = new NativeDecimalFormat(currentPattern, currentLocaleData);
    570             }
    571             if (!pattern.equals(currentPattern)) {
    572                 decimalFormat.applyPattern(pattern);
    573                 currentPattern = pattern;
    574             }
    575             if (localeData != currentLocaleData) {
    576                 decimalFormat.setDecimalFormatSymbols(localeData);
    577                 currentLocaleData = localeData;
    578             }
    579             return decimalFormat;
    580         }
    581     }
    582 
    583     private static final ThreadLocal<CachedDecimalFormat> cachedDecimalFormat = new ThreadLocal<CachedDecimalFormat>() {
    584         @Override protected CachedDecimalFormat initialValue() {
    585             return new CachedDecimalFormat();
    586         }
    587     };
    588 
    589     /**
    590      * Creates a native peer if we don't already have one, or reconfigures an existing one.
    591      * This means we get to reuse the peer in cases like "x=%.2f y=%.2f".
    592      */
    593     private NativeDecimalFormat getDecimalFormat(String pattern) {
    594         return cachedDecimalFormat.get().update(localeData, pattern);
    595     }
    596 
    597     /**
    598      * Constructs a {@code Formatter}.
    599      *
    600      * <p>The output is written to a {@code StringBuilder} which can be acquired by invoking
    601      * {@link #out()} and whose content can be obtained by calling {@code toString}.
    602      *
    603      * <p>The {@code Locale} used is the user's default locale.
    604      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    605      */
    606     public Formatter() {
    607         this(new StringBuilder(), Locale.getDefault());
    608     }
    609 
    610     /**
    611      * Constructs a {@code Formatter} whose output will be written to the
    612      * specified {@code Appendable}.
    613      *
    614      * <p>The {@code Locale} used is the user's default locale.
    615      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    616      *
    617      * @param a
    618      *            the output destination of the {@code Formatter}. If {@code a} is {@code null},
    619      *            then a {@code StringBuilder} will be used.
    620      */
    621     public Formatter(Appendable a) {
    622         this(a, Locale.getDefault());
    623     }
    624 
    625     /**
    626      * Constructs a {@code Formatter} with the specified {@code Locale}.
    627      *
    628      * <p>The output is written to a {@code StringBuilder} which can be acquired by invoking
    629      * {@link #out()} and whose content can be obtained by calling {@code toString}.
    630      *
    631      * @param l
    632      *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
    633      *            then no localization will be used.
    634      */
    635     public Formatter(Locale l) {
    636         this(new StringBuilder(), l);
    637     }
    638 
    639     /**
    640      * Constructs a {@code Formatter} with the specified {@code Locale}
    641      * and whose output will be written to the
    642      * specified {@code Appendable}.
    643      *
    644      * @param a
    645      *            the output destination of the {@code Formatter}. If {@code a} is {@code null},
    646      *            then a {@code StringBuilder} will be used.
    647      * @param l
    648      *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
    649      *            then no localization will be used.
    650      */
    651     public Formatter(Appendable a, Locale l) {
    652         if (a == null) {
    653             out = new StringBuilder();
    654         } else {
    655             out = a;
    656         }
    657         locale = l;
    658     }
    659 
    660     /**
    661      * Constructs a {@code Formatter} whose output is written to the specified file.
    662      *
    663      * <p>The charset of the {@code Formatter} is the default charset.
    664      *
    665      * <p>The {@code Locale} used is the user's default locale.
    666      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    667      *
    668      * @param fileName
    669      *            the filename of the file that is used as the output
    670      *            destination for the {@code Formatter}. The file will be truncated to
    671      *            zero size if the file exists, or else a new file will be
    672      *            created. The output of the {@code Formatter} is buffered.
    673      * @throws FileNotFoundException
    674      *             if the filename does not denote a normal and writable file,
    675      *             or if a new file cannot be created, or if any error arises when
    676      *             opening or creating the file.
    677      */
    678     public Formatter(String fileName) throws FileNotFoundException {
    679         this(new File(fileName));
    680 
    681     }
    682 
    683     /**
    684      * Constructs a {@code Formatter} whose output is written to the specified file.
    685      *
    686      * <p>The {@code Locale} used is the user's default locale.
    687      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    688      *
    689      * @param fileName
    690      *            the filename of the file that is used as the output
    691      *            destination for the {@code Formatter}. The file will be truncated to
    692      *            zero size if the file exists, or else a new file will be
    693      *            created. The output of the {@code Formatter} is buffered.
    694      * @param csn
    695      *            the name of the charset for the {@code Formatter}.
    696      * @throws FileNotFoundException
    697      *             if the filename does not denote a normal and writable file,
    698      *             or if a new file cannot be created, or if any error arises when
    699      *             opening or creating the file.
    700      * @throws UnsupportedEncodingException
    701      *             if the charset with the specified name is not supported.
    702      */
    703     public Formatter(String fileName, String csn) throws FileNotFoundException,
    704             UnsupportedEncodingException {
    705         this(new File(fileName), csn);
    706     }
    707 
    708     /**
    709      * Constructs a {@code Formatter} with the given {@code Locale} and charset,
    710      * and whose output is written to the specified file.
    711      *
    712      * @param fileName
    713      *            the filename of the file that is used as the output
    714      *            destination for the {@code Formatter}. The file will be truncated to
    715      *            zero size if the file exists, or else a new file will be
    716      *            created. The output of the {@code Formatter} is buffered.
    717      * @param csn
    718      *            the name of the charset for the {@code Formatter}.
    719      * @param l
    720      *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
    721      *            then no localization will be used.
    722      * @throws FileNotFoundException
    723      *             if the filename does not denote a normal and writable file,
    724      *             or if a new file cannot be created, or if any error arises when
    725      *             opening or creating the file.
    726      * @throws UnsupportedEncodingException
    727      *             if the charset with the specified name is not supported.
    728      */
    729     public Formatter(String fileName, String csn, Locale l)
    730             throws FileNotFoundException, UnsupportedEncodingException {
    731 
    732         this(new File(fileName), csn, l);
    733     }
    734 
    735     /**
    736      * Constructs a {@code Formatter} whose output is written to the specified {@code File}.
    737      *
    738      * The charset of the {@code Formatter} is the default charset.
    739      *
    740      * <p>The {@code Locale} used is the user's default locale.
    741      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    742      *
    743      * @param file
    744      *            the {@code File} that is used as the output destination for the
    745      *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
    746      *            exists, or else a new {@code File} will be created. The output of the
    747      *            {@code Formatter} is buffered.
    748      * @throws FileNotFoundException
    749      *             if the {@code File} is not a normal and writable {@code File}, or if a
    750      *             new {@code File} cannot be created, or if any error rises when opening or
    751      *             creating the {@code File}.
    752      */
    753     public Formatter(File file) throws FileNotFoundException {
    754         this(new FileOutputStream(file));
    755     }
    756 
    757     /**
    758      * Constructs a {@code Formatter} with the given charset,
    759      * and whose output is written to the specified {@code File}.
    760      *
    761      * <p>The {@code Locale} used is the user's default locale.
    762      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    763      *
    764      * @param file
    765      *            the {@code File} that is used as the output destination for the
    766      *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
    767      *            exists, or else a new {@code File} will be created. The output of the
    768      *            {@code Formatter} is buffered.
    769      * @param csn
    770      *            the name of the charset for the {@code Formatter}.
    771      * @throws FileNotFoundException
    772      *             if the {@code File} is not a normal and writable {@code File}, or if a
    773      *             new {@code File} cannot be created, or if any error rises when opening or
    774      *             creating the {@code File}.
    775      * @throws UnsupportedEncodingException
    776      *             if the charset with the specified name is not supported.
    777      */
    778     public Formatter(File file, String csn) throws FileNotFoundException,
    779             UnsupportedEncodingException {
    780         this(file, csn, Locale.getDefault());
    781     }
    782 
    783     /**
    784      * Constructs a {@code Formatter} with the given {@code Locale} and charset,
    785      * and whose output is written to the specified {@code File}.
    786      *
    787      * @param file
    788      *            the {@code File} that is used as the output destination for the
    789      *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
    790      *            exists, or else a new {@code File} will be created. The output of the
    791      *            {@code Formatter} is buffered.
    792      * @param csn
    793      *            the name of the charset for the {@code Formatter}.
    794      * @param l
    795      *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
    796      *            then no localization will be used.
    797      * @throws FileNotFoundException
    798      *             if the {@code File} is not a normal and writable {@code File}, or if a
    799      *             new {@code File} cannot be created, or if any error rises when opening or
    800      *             creating the {@code File}.
    801      * @throws UnsupportedEncodingException
    802      *             if the charset with the specified name is not supported.
    803      */
    804     public Formatter(File file, String csn, Locale l)
    805             throws FileNotFoundException, UnsupportedEncodingException {
    806         FileOutputStream fout = null;
    807         try {
    808             fout = new FileOutputStream(file);
    809             out = new BufferedWriter(new OutputStreamWriter(fout, csn));
    810         } catch (RuntimeException e) {
    811             IoUtils.closeQuietly(fout);
    812             throw e;
    813         } catch (UnsupportedEncodingException e) {
    814             IoUtils.closeQuietly(fout);
    815             throw e;
    816         }
    817 
    818         locale = l;
    819     }
    820 
    821     /**
    822      * Constructs a {@code Formatter} whose output is written to the specified {@code OutputStream}.
    823      *
    824      * <p>The charset of the {@code Formatter} is the default charset.
    825      *
    826      * <p>The {@code Locale} used is the user's default locale.
    827      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    828      *
    829      * @param os
    830      *            the stream to be used as the destination of the {@code Formatter}.
    831      */
    832     public Formatter(OutputStream os) {
    833         out = new BufferedWriter(new OutputStreamWriter(os, Charset.defaultCharset()));
    834         locale = Locale.getDefault();
    835     }
    836 
    837     /**
    838      * Constructs a {@code Formatter} with the given charset,
    839      * and whose output is written to the specified {@code OutputStream}.
    840      *
    841      * <p>The {@code Locale} used is the user's default locale.
    842      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    843      *
    844      * @param os
    845      *            the stream to be used as the destination of the {@code Formatter}.
    846      * @param csn
    847      *            the name of the charset for the {@code Formatter}.
    848      * @throws UnsupportedEncodingException
    849      *             if the charset with the specified name is not supported.
    850      */
    851     public Formatter(OutputStream os, String csn) throws UnsupportedEncodingException {
    852         this(os, csn, Locale.getDefault());
    853     }
    854 
    855     /**
    856      * Constructs a {@code Formatter} with the given {@code Locale} and charset,
    857      * and whose output is written to the specified {@code OutputStream}.
    858      *
    859      * @param os
    860      *            the stream to be used as the destination of the {@code Formatter}.
    861      * @param csn
    862      *            the name of the charset for the {@code Formatter}.
    863      * @param l
    864      *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
    865      *            then no localization will be used.
    866      * @throws UnsupportedEncodingException
    867      *             if the charset with the specified name is not supported.
    868      */
    869     public Formatter(OutputStream os, String csn, Locale l) throws UnsupportedEncodingException {
    870         out = new BufferedWriter(new OutputStreamWriter(os, csn));
    871         locale = l;
    872     }
    873 
    874     /**
    875      * Constructs a {@code Formatter} whose output is written to the specified {@code PrintStream}.
    876      *
    877      * <p>The charset of the {@code Formatter} is the default charset.
    878      *
    879      * <p>The {@code Locale} used is the user's default locale.
    880      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    881      *
    882      * @param ps
    883      *            the {@code PrintStream} used as destination of the {@code Formatter}. If
    884      *            {@code ps} is {@code null}, then a {@code NullPointerException} will
    885      *            be raised.
    886      */
    887     public Formatter(PrintStream ps) {
    888         if (ps == null) {
    889             throw new NullPointerException("ps == null");
    890         }
    891         out = ps;
    892         locale = Locale.getDefault();
    893     }
    894 
    895     private void checkNotClosed() {
    896         if (closed) {
    897             throw new FormatterClosedException();
    898         }
    899     }
    900 
    901     /**
    902      * Returns the {@code Locale} of the {@code Formatter}.
    903      *
    904      * @return the {@code Locale} for the {@code Formatter} or {@code null} for no {@code Locale}.
    905      * @throws FormatterClosedException
    906      *             if the {@code Formatter} has been closed.
    907      */
    908     public Locale locale() {
    909         checkNotClosed();
    910         return locale;
    911     }
    912 
    913     /**
    914      * Returns the output destination of the {@code Formatter}.
    915      *
    916      * @return the output destination of the {@code Formatter}.
    917      * @throws FormatterClosedException
    918      *             if the {@code Formatter} has been closed.
    919      */
    920     public Appendable out() {
    921         checkNotClosed();
    922         return out;
    923     }
    924 
    925     /**
    926      * Returns the content by calling the {@code toString()} method of the output
    927      * destination.
    928      *
    929      * @return the content by calling the {@code toString()} method of the output
    930      *         destination.
    931      * @throws FormatterClosedException
    932      *             if the {@code Formatter} has been closed.
    933      */
    934     @Override
    935     public String toString() {
    936         checkNotClosed();
    937         return out.toString();
    938     }
    939 
    940     /**
    941      * Flushes the {@code Formatter}. If the output destination is {@link Flushable},
    942      * then the method {@code flush()} will be called on that destination.
    943      *
    944      * @throws FormatterClosedException
    945      *             if the {@code Formatter} has been closed.
    946      */
    947     public void flush() {
    948         checkNotClosed();
    949         if (out instanceof Flushable) {
    950             try {
    951                 ((Flushable) out).flush();
    952             } catch (IOException e) {
    953                 lastIOException = e;
    954             }
    955         }
    956     }
    957 
    958     /**
    959      * Closes the {@code Formatter}. If the output destination is {@link Closeable},
    960      * then the method {@code close()} will be called on that destination.
    961      *
    962      * If the {@code Formatter} has been closed, then calling the this method will have no
    963      * effect.
    964      *
    965      * Any method but the {@link #ioException()} that is called after the
    966      * {@code Formatter} has been closed will raise a {@code FormatterClosedException}.
    967      */
    968     public void close() {
    969         if (!closed) {
    970             closed = true;
    971             try {
    972                 if (out instanceof Closeable) {
    973                     ((Closeable) out).close();
    974                 }
    975             } catch (IOException e) {
    976                 lastIOException = e;
    977             }
    978         }
    979     }
    980 
    981     /**
    982      * Returns the last {@code IOException} thrown by the {@code Formatter}'s output
    983      * destination. If the {@code append()} method of the destination does not throw
    984      * {@code IOException}s, the {@code ioException()} method will always return {@code null}.
    985      *
    986      * @return the last {@code IOException} thrown by the {@code Formatter}'s output
    987      *         destination.
    988      */
    989     public IOException ioException() {
    990         return lastIOException;
    991     }
    992 
    993     /**
    994      * Writes a formatted string to the output destination of the {@code Formatter}.
    995      *
    996      * @param format
    997      *            a format string.
    998      * @param args
    999      *            the arguments list used in the {@code format()} method. If there are
   1000      *            more arguments than those specified by the format string, then
   1001      *            the additional arguments are ignored.
   1002      * @return this {@code Formatter}.
   1003      * @throws IllegalFormatException
   1004      *             if the format string is illegal or incompatible with the
   1005      *             arguments, or if fewer arguments are sent than those required by
   1006      *             the format string, or any other illegal situation.
   1007      * @throws FormatterClosedException
   1008      *             if the {@code Formatter} has been closed.
   1009      */
   1010     public Formatter format(String format, Object... args) {
   1011         return format(this.locale, format, args);
   1012     }
   1013 
   1014     /**
   1015      * Writes a formatted string to the output destination of the {@code Formatter}.
   1016      *
   1017      * @param l
   1018      *            the {@code Locale} used in the method. If {@code locale} is
   1019      *            {@code null}, then no localization will be applied. This
   1020      *            parameter does not change this Formatter's default {@code Locale}
   1021      *            as specified during construction, and only applies for the
   1022      *            duration of this call.
   1023      * @param format
   1024      *            a format string.
   1025      * @param args
   1026      *            the arguments list used in the {@code format()} method. If there are
   1027      *            more arguments than those specified by the format string, then
   1028      *            the additional arguments are ignored.
   1029      * @return this {@code Formatter}.
   1030      * @throws IllegalFormatException
   1031      *             if the format string is illegal or incompatible with the
   1032      *             arguments, or if fewer arguments are sent than those required by
   1033      *             the format string, or any other illegal situation.
   1034      * @throws FormatterClosedException
   1035      *             if the {@code Formatter} has been closed.
   1036      */
   1037     public Formatter format(Locale l, String format, Object... args) {
   1038         Locale originalLocale = locale;
   1039         try {
   1040             this.locale = (l == null ? Locale.US : l);
   1041             this.localeData = LocaleData.get(locale);
   1042             doFormat(format, args);
   1043         } finally {
   1044             this.locale = originalLocale;
   1045         }
   1046         return this;
   1047     }
   1048 
   1049     private void doFormat(String format, Object... args) {
   1050         checkNotClosed();
   1051 
   1052         FormatSpecifierParser fsp = new FormatSpecifierParser(format);
   1053         int currentObjectIndex = 0;
   1054         Object lastArgument = null;
   1055         boolean hasLastArgumentSet = false;
   1056 
   1057         int length = format.length();
   1058         int i = 0;
   1059         while (i < length) {
   1060             // Find the maximal plain-text sequence...
   1061             int plainTextStart = i;
   1062             int nextPercent = format.indexOf('%', i);
   1063             int plainTextEnd = (nextPercent == -1) ? length : nextPercent;
   1064             // ...and output it.
   1065             if (plainTextEnd > plainTextStart) {
   1066                 outputCharSequence(format, plainTextStart, plainTextEnd);
   1067             }
   1068             i = plainTextEnd;
   1069             // Do we have a format specifier?
   1070             if (i < length) {
   1071                 FormatToken token = fsp.parseFormatToken(i + 1);
   1072 
   1073                 Object argument = null;
   1074                 if (token.requireArgument()) {
   1075                     int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++ : token.getArgIndex();
   1076                     argument = getArgument(args, index, fsp, lastArgument, hasLastArgumentSet);
   1077                     lastArgument = argument;
   1078                     hasLastArgumentSet = true;
   1079                 }
   1080 
   1081                 CharSequence substitution = transform(token, argument);
   1082                 // The substitution is null if we called Formattable.formatTo.
   1083                 if (substitution != null) {
   1084                     outputCharSequence(substitution, 0, substitution.length());
   1085                 }
   1086                 i = fsp.i;
   1087             }
   1088         }
   1089     }
   1090 
   1091     // Fixes http://code.google.com/p/android/issues/detail?id=1767.
   1092     private void outputCharSequence(CharSequence cs, int start, int end) {
   1093         try {
   1094             out.append(cs, start, end);
   1095         } catch (IOException e) {
   1096             lastIOException = e;
   1097         }
   1098     }
   1099 
   1100     private Object getArgument(Object[] args, int index, FormatSpecifierParser fsp,
   1101             Object lastArgument, boolean hasLastArgumentSet) {
   1102         if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) {
   1103             throw new MissingFormatArgumentException("<");
   1104         }
   1105 
   1106         if (args == null) {
   1107             return null;
   1108         }
   1109 
   1110         if (index >= args.length) {
   1111             throw new MissingFormatArgumentException(fsp.getFormatSpecifierText());
   1112         }
   1113 
   1114         if (index == FormatToken.LAST_ARGUMENT_INDEX) {
   1115             return lastArgument;
   1116         }
   1117 
   1118         return args[index];
   1119     }
   1120 
   1121     /*
   1122      * Complete details of a single format specifier parsed from a format string.
   1123      */
   1124     private static class FormatToken {
   1125         static final int LAST_ARGUMENT_INDEX = -2;
   1126 
   1127         static final int UNSET = -1;
   1128 
   1129         static final int FLAGS_UNSET = 0;
   1130 
   1131         static final int DEFAULT_PRECISION = 6;
   1132 
   1133         static final int FLAG_ZERO = 1 << 4;
   1134 
   1135         private int argIndex = UNSET;
   1136 
   1137         // These have package access for performance. They used to be represented by an int bitmask
   1138         // and accessed via methods, but Android's JIT doesn't yet do a good job of such code.
   1139         // Direct field access, on the other hand, is fast.
   1140         boolean flagComma;
   1141         boolean flagMinus;
   1142         boolean flagParenthesis;
   1143         boolean flagPlus;
   1144         boolean flagSharp;
   1145         boolean flagSpace;
   1146         boolean flagZero;
   1147 
   1148         private char conversionType = (char) UNSET;
   1149         private char dateSuffix;
   1150 
   1151         private int precision = UNSET;
   1152         private int width = UNSET;
   1153 
   1154         private StringBuilder strFlags;
   1155 
   1156         // Tests whether there were no flags, no width, and no precision specified.
   1157         boolean isDefault() {
   1158             return !flagComma && !flagMinus && !flagParenthesis && !flagPlus && !flagSharp &&
   1159                     !flagSpace && !flagZero && width == UNSET && precision == UNSET;
   1160         }
   1161 
   1162         boolean isPrecisionSet() {
   1163             return precision != UNSET;
   1164         }
   1165 
   1166         int getArgIndex() {
   1167             return argIndex;
   1168         }
   1169 
   1170         void setArgIndex(int index) {
   1171             argIndex = index;
   1172         }
   1173 
   1174         int getWidth() {
   1175             return width;
   1176         }
   1177 
   1178         void setWidth(int width) {
   1179             this.width = width;
   1180         }
   1181 
   1182         int getPrecision() {
   1183             return precision;
   1184         }
   1185 
   1186         void setPrecision(int precise) {
   1187             this.precision = precise;
   1188         }
   1189 
   1190         String getStrFlags() {
   1191             return (strFlags != null) ? strFlags.toString() : "";
   1192         }
   1193 
   1194         /*
   1195          * Sets qualified char as one of the flags. If the char is qualified,
   1196          * sets it as a flag and returns true. Or else returns false.
   1197          */
   1198         boolean setFlag(int ch) {
   1199             boolean dupe = false;
   1200             switch (ch) {
   1201             case ',':
   1202                 dupe = flagComma;
   1203                 flagComma = true;
   1204                 break;
   1205             case '-':
   1206                 dupe = flagMinus;
   1207                 flagMinus = true;
   1208                 break;
   1209             case '(':
   1210                 dupe = flagParenthesis;
   1211                 flagParenthesis = true;
   1212                 break;
   1213             case '+':
   1214                 dupe = flagPlus;
   1215                 flagPlus = true;
   1216                 break;
   1217             case '#':
   1218                 dupe = flagSharp;
   1219                 flagSharp = true;
   1220                 break;
   1221             case ' ':
   1222                 dupe = flagSpace;
   1223                 flagSpace = true;
   1224                 break;
   1225             case '0':
   1226                 dupe = flagZero;
   1227                 flagZero = true;
   1228                 break;
   1229             default:
   1230                 return false;
   1231             }
   1232             if (dupe) {
   1233                 // The RI documentation implies we're supposed to report all the flags, not just
   1234                 // the first duplicate, but the RI behaves the same as we do.
   1235                 throw new DuplicateFormatFlagsException(String.valueOf(ch));
   1236             }
   1237             if (strFlags == null) {
   1238                 strFlags = new StringBuilder(7); // There are seven possible flags.
   1239             }
   1240             strFlags.append((char) ch);
   1241             return true;
   1242         }
   1243 
   1244         char getConversionType() {
   1245             return conversionType;
   1246         }
   1247 
   1248         void setConversionType(char c) {
   1249             conversionType = c;
   1250         }
   1251 
   1252         char getDateSuffix() {
   1253             return dateSuffix;
   1254         }
   1255 
   1256         void setDateSuffix(char c) {
   1257             dateSuffix = c;
   1258         }
   1259 
   1260         boolean requireArgument() {
   1261             return conversionType != '%' && conversionType != 'n';
   1262         }
   1263 
   1264         void checkFlags(Object arg) {
   1265             // Work out which flags are allowed.
   1266             boolean allowComma = false;
   1267             boolean allowMinus = true;
   1268             boolean allowParenthesis = false;
   1269             boolean allowPlus = false;
   1270             boolean allowSharp = false;
   1271             boolean allowSpace = false;
   1272             boolean allowZero = false;
   1273             // Precision and width?
   1274             boolean allowPrecision = true;
   1275             boolean allowWidth = true;
   1276             // Argument?
   1277             boolean allowArgument = true;
   1278             switch (conversionType) {
   1279             // Character and date/time.
   1280             case 'c': case 'C': case 't': case 'T':
   1281                 // Only '-' is allowed.
   1282                 allowPrecision = false;
   1283                 break;
   1284 
   1285             // String.
   1286             case 's': case 'S':
   1287                 if (arg instanceof Formattable) {
   1288                     allowSharp = true;
   1289                 }
   1290                 break;
   1291 
   1292             // Floating point.
   1293             case 'g': case 'G':
   1294                 allowComma = allowParenthesis = allowPlus = allowSpace = allowZero = true;
   1295                 break;
   1296             case 'f':
   1297                 allowComma = allowParenthesis = allowPlus = allowSharp = allowSpace = allowZero = true;
   1298                 break;
   1299             case 'e': case 'E':
   1300                 allowParenthesis = allowPlus = allowSharp = allowSpace = allowZero = true;
   1301                 break;
   1302             case 'a': case 'A':
   1303                 allowPlus = allowSharp = allowSpace = allowZero = true;
   1304                 break;
   1305 
   1306             // Integral.
   1307             case 'd':
   1308                 allowComma = allowParenthesis = allowPlus = allowSpace = allowZero = true;
   1309                 allowPrecision = false;
   1310                 break;
   1311             case 'o': case 'x': case 'X':
   1312                 allowSharp = allowZero = true;
   1313                 if (arg == null || arg instanceof BigInteger) {
   1314                     allowParenthesis = allowPlus = allowSpace = true;
   1315                 }
   1316                 allowPrecision = false;
   1317                 break;
   1318 
   1319             // Special.
   1320             case 'n':
   1321                 // Nothing is allowed.
   1322                 allowMinus = false;
   1323                 allowArgument = allowPrecision = allowWidth = false;
   1324                 break;
   1325             case '%':
   1326                 // The only flag allowed is '-', and no argument or precision is allowed.
   1327                 allowArgument = false;
   1328                 allowPrecision = false;
   1329                 break;
   1330 
   1331             // Booleans and hash codes.
   1332             case 'b': case 'B': case 'h': case 'H':
   1333                 break;
   1334 
   1335             default:
   1336                 throw unknownFormatConversionException();
   1337             }
   1338 
   1339             // Check for disallowed flags.
   1340             String mismatch = null;
   1341             if (!allowComma && flagComma) {
   1342                 mismatch = ",";
   1343             } else if (!allowMinus && flagMinus) {
   1344                 mismatch = "-";
   1345             } else if (!allowParenthesis && flagParenthesis) {
   1346                 mismatch = "(";
   1347             } else if (!allowPlus && flagPlus) {
   1348                 mismatch = "+";
   1349             } else if (!allowSharp && flagSharp) {
   1350                 mismatch = "#";
   1351             } else if (!allowSpace && flagSpace) {
   1352                 mismatch = " ";
   1353             } else if (!allowZero && flagZero) {
   1354                 mismatch = "0";
   1355             }
   1356             if (mismatch != null) {
   1357                 if (conversionType == 'n') {
   1358                     // For no good reason, %n is a special case...
   1359                     throw new IllegalFormatFlagsException(mismatch);
   1360                 } else {
   1361                     throw new FormatFlagsConversionMismatchException(mismatch, conversionType);
   1362                 }
   1363             }
   1364 
   1365             // Check for a missing width with flags that require a width.
   1366             if ((flagMinus || flagZero) && width == UNSET) {
   1367                 throw new MissingFormatWidthException("-" + conversionType);
   1368             }
   1369 
   1370             // Check that no-argument conversion types don't have an argument.
   1371             // Note: the RI doesn't enforce this.
   1372             if (!allowArgument && argIndex != UNSET) {
   1373                 throw new IllegalFormatFlagsException("%" + conversionType +
   1374                         " doesn't take an argument");
   1375             }
   1376 
   1377             // Check that we don't have a precision or width where they're not allowed.
   1378             if (!allowPrecision && precision != UNSET) {
   1379                 throw new IllegalFormatPrecisionException(precision);
   1380             }
   1381             if (!allowWidth && width != UNSET) {
   1382                 throw new IllegalFormatWidthException(width);
   1383             }
   1384 
   1385             // Some combinations make no sense...
   1386             if (flagPlus && flagSpace) {
   1387                 throw new IllegalFormatFlagsException("the '+' and ' ' flags are incompatible");
   1388             }
   1389             if (flagMinus && flagZero) {
   1390                 throw new IllegalFormatFlagsException("the '-' and '0' flags are incompatible");
   1391             }
   1392         }
   1393 
   1394         public UnknownFormatConversionException unknownFormatConversionException() {
   1395             if (conversionType == 't' || conversionType == 'T') {
   1396                 throw new UnknownFormatConversionException(String.format("%c%c",
   1397                         conversionType, dateSuffix));
   1398             }
   1399             throw new UnknownFormatConversionException(String.valueOf(conversionType));
   1400         }
   1401     }
   1402 
   1403     /*
   1404      * Gets the formatted string according to the format token and the
   1405      * argument.
   1406      */
   1407     private CharSequence transform(FormatToken token, Object argument) {
   1408         this.formatToken = token;
   1409         this.arg = argument;
   1410 
   1411         // There are only two format specifiers that matter: "%d" and "%s".
   1412         // Nothing else is common in the wild. We fast-path these two to
   1413         // avoid the heavyweight machinery needed to cope with flags, width,
   1414         // and precision.
   1415         if (token.isDefault()) {
   1416             switch (token.getConversionType()) {
   1417             case 's':
   1418                 if (arg == null) {
   1419                     return "null";
   1420                 } else if (!(arg instanceof Formattable)) {
   1421                     return arg.toString();
   1422                 }
   1423                 break;
   1424             case 'd':
   1425                 boolean needLocalizedDigits = (localeData.zeroDigit != '0');
   1426                 if (out instanceof StringBuilder && !needLocalizedDigits) {
   1427                     if (arg instanceof Integer || arg instanceof Short || arg instanceof Byte) {
   1428                         IntegralToString.appendInt((StringBuilder) out, ((Number) arg).intValue());
   1429                         return null;
   1430                     } else if (arg instanceof Long) {
   1431                         IntegralToString.appendLong((StringBuilder) out, ((Long) arg).longValue());
   1432                         return null;
   1433                     }
   1434                 }
   1435                 if (arg instanceof Integer || arg instanceof Long || arg instanceof Short || arg instanceof Byte) {
   1436                     String result = arg.toString();
   1437                     return needLocalizedDigits ? localizeDigits(result) : result;
   1438                 }
   1439             }
   1440         }
   1441 
   1442         formatToken.checkFlags(arg);
   1443         CharSequence result;
   1444         switch (token.getConversionType()) {
   1445         case 'B': case 'b':
   1446             result = transformFromBoolean();
   1447             break;
   1448         case 'H': case 'h':
   1449             result = transformFromHashCode();
   1450             break;
   1451         case 'S': case 's':
   1452             result = transformFromString();
   1453             break;
   1454         case 'C': case 'c':
   1455             result = transformFromCharacter();
   1456             break;
   1457         case 'd': case 'o': case 'x': case 'X':
   1458             if (arg == null || arg instanceof BigInteger) {
   1459                 result = transformFromBigInteger();
   1460             } else {
   1461                 result = transformFromInteger();
   1462             }
   1463             break;
   1464         case 'A': case 'a': case 'E': case 'e': case 'f': case 'G': case 'g':
   1465             result = transformFromFloat();
   1466             break;
   1467         case '%':
   1468             result = transformFromPercent();
   1469             break;
   1470         case 'n':
   1471             result = System.lineSeparator();
   1472             break;
   1473         case 't': case 'T':
   1474             result = transformFromDateTime();
   1475             break;
   1476         default:
   1477             throw token.unknownFormatConversionException();
   1478         }
   1479 
   1480         if (Character.isUpperCase(token.getConversionType())) {
   1481             if (result != null) {
   1482                 result = result.toString().toUpperCase(locale);
   1483             }
   1484         }
   1485         return result;
   1486     }
   1487 
   1488     private IllegalFormatConversionException badArgumentType() {
   1489         throw new IllegalFormatConversionException(formatToken.getConversionType(), arg.getClass());
   1490     }
   1491 
   1492     /**
   1493      * Returns a CharSequence corresponding to {@code s} with all the ASCII digits replaced
   1494      * by digits appropriate to this formatter's locale. Other characters remain unchanged.
   1495      */
   1496     private CharSequence localizeDigits(CharSequence s) {
   1497         int length = s.length();
   1498         int offsetToLocalizedDigits = localeData.zeroDigit - '0';
   1499         StringBuilder result = new StringBuilder(length);
   1500         for (int i = 0; i < length; ++i) {
   1501             char ch = s.charAt(i);
   1502             if (ch >= '0' && ch <= '9') {
   1503                 ch += offsetToLocalizedDigits;
   1504             }
   1505             result.append(ch);
   1506         }
   1507         return result;
   1508     }
   1509 
   1510     /**
   1511      * Inserts the grouping separator every 3 digits. DecimalFormat lets you configure grouping
   1512      * size, but you can't access that from Formatter, and the default is every 3 digits.
   1513      */
   1514     private CharSequence insertGrouping(CharSequence s) {
   1515         StringBuilder result = new StringBuilder(s.length() + s.length()/3);
   1516 
   1517         // A leading '-' doesn't want to be included in the grouping.
   1518         int digitsLength = s.length();
   1519         int i = 0;
   1520         if (s.charAt(0) == '-') {
   1521             --digitsLength;
   1522             ++i;
   1523             result.append('-');
   1524         }
   1525 
   1526         // Append the digits that come before the first separator.
   1527         int headLength = digitsLength % 3;
   1528         if (headLength == 0) {
   1529             headLength = 3;
   1530         }
   1531         result.append(s, i, i + headLength);
   1532         i += headLength;
   1533 
   1534         // Append the remaining groups.
   1535         for (; i < s.length(); i += 3) {
   1536             result.append(localeData.groupingSeparator);
   1537             result.append(s, i, i + 3);
   1538         }
   1539         return result;
   1540     }
   1541 
   1542     private CharSequence transformFromBoolean() {
   1543         CharSequence result;
   1544         if (arg instanceof Boolean) {
   1545             result = arg.toString();
   1546         } else if (arg == null) {
   1547             result = "false";
   1548         } else {
   1549             result = "true";
   1550         }
   1551         return padding(result, 0);
   1552     }
   1553 
   1554     private CharSequence transformFromHashCode() {
   1555         CharSequence result;
   1556         if (arg == null) {
   1557             result = "null";
   1558         } else {
   1559             result = Integer.toHexString(arg.hashCode());
   1560         }
   1561         return padding(result, 0);
   1562     }
   1563 
   1564     private CharSequence transformFromString() {
   1565         if (arg instanceof Formattable) {
   1566             int flags = 0;
   1567             if (formatToken.flagMinus) {
   1568                 flags |= FormattableFlags.LEFT_JUSTIFY;
   1569             }
   1570             if (formatToken.flagSharp) {
   1571                 flags |= FormattableFlags.ALTERNATE;
   1572             }
   1573             if (Character.isUpperCase(formatToken.getConversionType())) {
   1574                 flags |= FormattableFlags.UPPERCASE;
   1575             }
   1576             ((Formattable) arg).formatTo(this, flags, formatToken.getWidth(),
   1577                     formatToken.getPrecision());
   1578             // all actions have been taken out in the
   1579             // Formattable.formatTo, thus there is nothing to do, just
   1580             // returns null, which tells the Parser to add nothing to the
   1581             // output.
   1582             return null;
   1583         }
   1584         CharSequence result = arg != null ? arg.toString() : "null";
   1585         return padding(result, 0);
   1586     }
   1587 
   1588     private CharSequence transformFromCharacter() {
   1589         if (arg == null) {
   1590             return padding("null", 0);
   1591         }
   1592         if (arg instanceof Character) {
   1593             return padding(String.valueOf(arg), 0);
   1594         } else if (arg instanceof Byte || arg instanceof Short || arg instanceof Integer) {
   1595             int codePoint = ((Number) arg).intValue();
   1596             if (!Character.isValidCodePoint(codePoint)) {
   1597                 throw new IllegalFormatCodePointException(codePoint);
   1598             }
   1599             CharSequence result = (codePoint < Character.MIN_SUPPLEMENTARY_CODE_POINT)
   1600                     ? String.valueOf((char) codePoint)
   1601                     : String.valueOf(Character.toChars(codePoint));
   1602             return padding(result, 0);
   1603         } else {
   1604             throw badArgumentType();
   1605         }
   1606     }
   1607 
   1608     private CharSequence transformFromPercent() {
   1609         return padding("%", 0);
   1610     }
   1611 
   1612     private CharSequence padding(CharSequence source, int startIndex) {
   1613         int start = startIndex;
   1614         int width = formatToken.getWidth();
   1615         int precision = formatToken.getPrecision();
   1616 
   1617         int length = source.length();
   1618         if (precision >= 0) {
   1619             length = Math.min(length, precision);
   1620             if (source instanceof StringBuilder) {
   1621                 ((StringBuilder) source).setLength(length);
   1622             } else {
   1623                 source = source.subSequence(0, length);
   1624             }
   1625         }
   1626         if (width > 0) {
   1627             width = Math.max(source.length(), width);
   1628         }
   1629         if (length >= width) {
   1630             return source;
   1631         }
   1632 
   1633         char paddingChar = '\u0020'; // space as padding char.
   1634         if (formatToken.flagZero) {
   1635             if (formatToken.getConversionType() == 'd') {
   1636                 paddingChar = localeData.zeroDigit;
   1637             } else {
   1638                 paddingChar = '0'; // No localized digits for bases other than decimal.
   1639             }
   1640         } else {
   1641             // if padding char is space, always pad from the start.
   1642             start = 0;
   1643         }
   1644         char[] paddingChars = new char[width - length];
   1645         Arrays.fill(paddingChars, paddingChar);
   1646 
   1647         boolean paddingRight = formatToken.flagMinus;
   1648         StringBuilder result = toStringBuilder(source);
   1649         if (paddingRight) {
   1650             result.append(paddingChars);
   1651         } else {
   1652             result.insert(start, paddingChars);
   1653         }
   1654         return result;
   1655     }
   1656 
   1657     private StringBuilder toStringBuilder(CharSequence cs) {
   1658         return cs instanceof StringBuilder ? (StringBuilder) cs : new StringBuilder(cs);
   1659     }
   1660 
   1661     private StringBuilder wrapParentheses(StringBuilder result) {
   1662         result.setCharAt(0, '('); // Replace the '-'.
   1663         if (formatToken.flagZero) {
   1664             formatToken.setWidth(formatToken.getWidth() - 1);
   1665             result = (StringBuilder) padding(result, 1);
   1666             result.append(')');
   1667         } else {
   1668             result.append(')');
   1669             result = (StringBuilder) padding(result, 0);
   1670         }
   1671         return result;
   1672     }
   1673 
   1674     private CharSequence transformFromInteger() {
   1675         int startIndex = 0;
   1676         StringBuilder result = new StringBuilder();
   1677         char currentConversionType = formatToken.getConversionType();
   1678 
   1679         long value;
   1680         if (arg instanceof Long) {
   1681             value = ((Long) arg).longValue();
   1682         } else if (arg instanceof Integer) {
   1683             value = ((Integer) arg).longValue();
   1684         } else if (arg instanceof Short) {
   1685             value = ((Short) arg).longValue();
   1686         } else if (arg instanceof Byte) {
   1687             value = ((Byte) arg).longValue();
   1688         } else {
   1689             throw badArgumentType();
   1690         }
   1691 
   1692         if (formatToken.flagSharp) {
   1693             if (currentConversionType == 'o') {
   1694                 result.append("0");
   1695                 startIndex += 1;
   1696             } else {
   1697                 result.append("0x");
   1698                 startIndex += 2;
   1699             }
   1700         }
   1701 
   1702         if (currentConversionType == 'd') {
   1703             CharSequence digits = Long.toString(value);
   1704             if (formatToken.flagComma) {
   1705                 digits = insertGrouping(digits);
   1706             }
   1707             if (localeData.zeroDigit != '0') {
   1708                 digits = localizeDigits(digits);
   1709             }
   1710             result.append(digits);
   1711 
   1712             if (value < 0) {
   1713                 if (formatToken.flagParenthesis) {
   1714                     return wrapParentheses(result);
   1715                 } else if (formatToken.flagZero) {
   1716                     startIndex++;
   1717                 }
   1718             } else {
   1719                 if (formatToken.flagPlus) {
   1720                     result.insert(0, '+');
   1721                     startIndex += 1;
   1722                 } else if (formatToken.flagSpace) {
   1723                     result.insert(0, ' ');
   1724                     startIndex += 1;
   1725                 }
   1726             }
   1727         } else {
   1728             // Undo sign-extension, since we'll be using Long.to(Octal|Hex)String.
   1729             if (arg instanceof Byte) {
   1730                 value &= 0xffL;
   1731             } else if (arg instanceof Short) {
   1732                 value &= 0xffffL;
   1733             } else if (arg instanceof Integer) {
   1734                 value &= 0xffffffffL;
   1735             }
   1736             if (currentConversionType == 'o') {
   1737                 result.append(Long.toOctalString(value));
   1738             } else {
   1739                 result.append(Long.toHexString(value));
   1740             }
   1741         }
   1742 
   1743         return padding(result, startIndex);
   1744     }
   1745 
   1746     private CharSequence transformFromNull() {
   1747         formatToken.flagZero = false;
   1748         return padding("null", 0);
   1749     }
   1750 
   1751     private CharSequence transformFromBigInteger() {
   1752         int startIndex = 0;
   1753         StringBuilder result = new StringBuilder();
   1754         BigInteger bigInt = (BigInteger) arg;
   1755         char currentConversionType = formatToken.getConversionType();
   1756 
   1757         if (bigInt == null) {
   1758             return transformFromNull();
   1759         }
   1760 
   1761         boolean isNegative = (bigInt.compareTo(BigInteger.ZERO) < 0);
   1762 
   1763         if (currentConversionType == 'd') {
   1764             CharSequence digits = bigInt.toString(10);
   1765             if (formatToken.flagComma) {
   1766                 digits = insertGrouping(digits);
   1767             }
   1768             result.append(digits);
   1769         } else if (currentConversionType == 'o') {
   1770             // convert BigInteger to a string presentation using radix 8
   1771             result.append(bigInt.toString(8));
   1772         } else {
   1773             // convert BigInteger to a string presentation using radix 16
   1774             result.append(bigInt.toString(16));
   1775         }
   1776         if (formatToken.flagSharp) {
   1777             startIndex = isNegative ? 1 : 0;
   1778             if (currentConversionType == 'o') {
   1779                 result.insert(startIndex, "0");
   1780                 startIndex += 1;
   1781             } else if (currentConversionType == 'x' || currentConversionType == 'X') {
   1782                 result.insert(startIndex, "0x");
   1783                 startIndex += 2;
   1784             }
   1785         }
   1786 
   1787         if (!isNegative) {
   1788             if (formatToken.flagPlus) {
   1789                 result.insert(0, '+');
   1790                 startIndex += 1;
   1791             }
   1792             if (formatToken.flagSpace) {
   1793                 result.insert(0, ' ');
   1794                 startIndex += 1;
   1795             }
   1796         }
   1797 
   1798         /* pad paddingChar to the output */
   1799         if (isNegative && formatToken.flagParenthesis) {
   1800             return wrapParentheses(result);
   1801         }
   1802         if (isNegative && formatToken.flagZero) {
   1803             startIndex++;
   1804         }
   1805         return padding(result, startIndex);
   1806     }
   1807 
   1808     private CharSequence transformFromDateTime() {
   1809         if (arg == null) {
   1810             return transformFromNull();
   1811         }
   1812 
   1813         Calendar calendar;
   1814         if (arg instanceof Calendar) {
   1815             calendar = (Calendar) arg;
   1816         } else {
   1817             Date date = null;
   1818             if (arg instanceof Long) {
   1819                 date = new Date(((Long) arg).longValue());
   1820             } else if (arg instanceof Date) {
   1821                 date = (Date) arg;
   1822             } else {
   1823                 throw badArgumentType();
   1824             }
   1825             calendar = Calendar.getInstance(locale);
   1826             calendar.setTime(date);
   1827         }
   1828 
   1829         StringBuilder result = new StringBuilder();
   1830         if (!appendT(result, formatToken.getDateSuffix(), calendar)) {
   1831             throw formatToken.unknownFormatConversionException();
   1832         }
   1833         return padding(result, 0);
   1834     }
   1835 
   1836     private boolean appendT(StringBuilder result, char conversion, Calendar calendar) {
   1837         switch (conversion) {
   1838         case 'A':
   1839             result.append(localeData.longWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]);
   1840             return true;
   1841         case 'a':
   1842             result.append(localeData.shortWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]);
   1843             return true;
   1844         case 'B':
   1845             result.append(localeData.longMonthNames[calendar.get(Calendar.MONTH)]);
   1846             return true;
   1847         case 'b': case 'h':
   1848             result.append(localeData.shortMonthNames[calendar.get(Calendar.MONTH)]);
   1849             return true;
   1850         case 'C':
   1851             appendLocalized(result, calendar.get(Calendar.YEAR) / 100, 2);
   1852             return true;
   1853         case 'D':
   1854             appendT(result, 'm', calendar);
   1855             result.append('/');
   1856             appendT(result, 'd', calendar);
   1857             result.append('/');
   1858             appendT(result, 'y', calendar);
   1859             return true;
   1860         case 'F':
   1861             appendT(result, 'Y', calendar);
   1862             result.append('-');
   1863             appendT(result, 'm', calendar);
   1864             result.append('-');
   1865             appendT(result, 'd', calendar);
   1866             return true;
   1867         case 'H':
   1868             appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 2);
   1869             return true;
   1870         case 'I':
   1871             appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 2);
   1872             return true;
   1873         case 'L':
   1874             appendLocalized(result, calendar.get(Calendar.MILLISECOND), 3);
   1875             return true;
   1876         case 'M':
   1877             appendLocalized(result, calendar.get(Calendar.MINUTE), 2);
   1878             return true;
   1879         case 'N':
   1880             appendLocalized(result, calendar.get(Calendar.MILLISECOND) * 1000000L, 9);
   1881             return true;
   1882         case 'Q':
   1883             appendLocalized(result, calendar.getTimeInMillis(), 0);
   1884             return true;
   1885         case 'R':
   1886             appendT(result, 'H', calendar);
   1887             result.append(':');
   1888             appendT(result, 'M', calendar);
   1889             return true;
   1890         case 'S':
   1891             appendLocalized(result, calendar.get(Calendar.SECOND), 2);
   1892             return true;
   1893         case 'T':
   1894             appendT(result, 'H', calendar);
   1895             result.append(':');
   1896             appendT(result, 'M', calendar);
   1897             result.append(':');
   1898             appendT(result, 'S', calendar);
   1899             return true;
   1900         case 'Y':
   1901             appendLocalized(result, calendar.get(Calendar.YEAR), 4);
   1902             return true;
   1903         case 'Z':
   1904             TimeZone timeZone = calendar.getTimeZone();
   1905             result.append(timeZone.getDisplayName(timeZone.inDaylightTime(calendar.getTime()),
   1906                     TimeZone.SHORT, locale));
   1907             return true;
   1908         case 'c':
   1909             appendT(result, 'a', calendar);
   1910             result.append(' ');
   1911             appendT(result, 'b', calendar);
   1912             result.append(' ');
   1913             appendT(result, 'd', calendar);
   1914             result.append(' ');
   1915             appendT(result, 'T', calendar);
   1916             result.append(' ');
   1917             appendT(result, 'Z', calendar);
   1918             result.append(' ');
   1919             appendT(result, 'Y', calendar);
   1920             return true;
   1921         case 'd':
   1922             appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 2);
   1923             return true;
   1924         case 'e':
   1925             appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 0);
   1926             return true;
   1927         case 'j':
   1928             appendLocalized(result, calendar.get(Calendar.DAY_OF_YEAR), 3);
   1929             return true;
   1930         case 'k':
   1931             appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 0);
   1932             return true;
   1933         case 'l':
   1934             appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 0);
   1935             return true;
   1936         case 'm':
   1937             // Calendar.JANUARY is 0; humans want January represented as 1.
   1938             appendLocalized(result, calendar.get(Calendar.MONTH) + 1, 2);
   1939             return true;
   1940         case 'p':
   1941             result.append(localeData.amPm[calendar.get(Calendar.AM_PM)].toLowerCase(locale));
   1942             return true;
   1943         case 'r':
   1944             appendT(result, 'I', calendar);
   1945             result.append(':');
   1946             appendT(result, 'M', calendar);
   1947             result.append(':');
   1948             appendT(result, 'S', calendar);
   1949             result.append(' ');
   1950             result.append(localeData.amPm[calendar.get(Calendar.AM_PM)]);
   1951             return true;
   1952         case 's':
   1953             appendLocalized(result, calendar.getTimeInMillis() / 1000, 0);
   1954             return true;
   1955         case 'y':
   1956             appendLocalized(result, calendar.get(Calendar.YEAR) % 100, 2);
   1957             return true;
   1958         case 'z':
   1959             long offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
   1960             char sign = '+';
   1961             if (offset < 0) {
   1962                 sign = '-';
   1963                 offset = -offset;
   1964             }
   1965             result.append(sign);
   1966             appendLocalized(result, offset / 3600000, 2);
   1967             appendLocalized(result, (offset % 3600000) / 60000, 2);
   1968             return true;
   1969         }
   1970         return false;
   1971     }
   1972 
   1973     private int to12Hour(int hour) {
   1974         return hour == 0 ? 12 : hour;
   1975     }
   1976 
   1977     private void appendLocalized(StringBuilder result, long value, int width) {
   1978         int paddingIndex = result.length();
   1979         char zeroDigit = localeData.zeroDigit;
   1980         if (zeroDigit == '0') {
   1981             result.append(value);
   1982         } else {
   1983             result.append(localizeDigits(Long.toString(value)));
   1984         }
   1985         int zeroCount = width - (result.length() - paddingIndex);
   1986         if (zeroCount <= 0) {
   1987             return;
   1988         }
   1989         if (zeroDigit == '0') {
   1990             result.insert(paddingIndex, ZEROS, 0, zeroCount);
   1991         } else {
   1992             for (int i = 0; i < zeroCount; ++i) {
   1993                 result.insert(paddingIndex, zeroDigit);
   1994             }
   1995         }
   1996     }
   1997 
   1998     private CharSequence transformFromSpecialNumber(double d) {
   1999         String source = null;
   2000         if (Double.isNaN(d)) {
   2001             source = "NaN";
   2002         } else if (d == Double.POSITIVE_INFINITY) {
   2003             if (formatToken.flagPlus) {
   2004                 source = "+Infinity";
   2005             } else if (formatToken.flagSpace) {
   2006                 source = " Infinity";
   2007             } else {
   2008                 source = "Infinity";
   2009             }
   2010         } else if (d == Double.NEGATIVE_INFINITY) {
   2011             if (formatToken.flagParenthesis) {
   2012                 source = "(Infinity)";
   2013             } else {
   2014                 source = "-Infinity";
   2015             }
   2016         } else {
   2017             return null;
   2018         }
   2019 
   2020         formatToken.setPrecision(FormatToken.UNSET);
   2021         formatToken.flagZero = false;
   2022         return padding(source, 0);
   2023     }
   2024 
   2025     private CharSequence transformFromFloat() {
   2026         if (arg == null) {
   2027             return transformFromNull();
   2028         } else if (arg instanceof Float || arg instanceof Double) {
   2029             Number number = (Number) arg;
   2030             double d = number.doubleValue();
   2031             if (d != d || d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY) {
   2032                 return transformFromSpecialNumber(d);
   2033             }
   2034         } else if (arg instanceof BigDecimal) {
   2035             // BigDecimal can't represent NaN or infinities, but its doubleValue method will return
   2036             // infinities if the BigDecimal is too big for a double.
   2037         } else {
   2038             throw badArgumentType();
   2039         }
   2040 
   2041         char conversionType = formatToken.getConversionType();
   2042         if (conversionType != 'a' && conversionType != 'A' && !formatToken.isPrecisionSet()) {
   2043             formatToken.setPrecision(FormatToken.DEFAULT_PRECISION);
   2044         }
   2045 
   2046         StringBuilder result = new StringBuilder();
   2047         switch (conversionType) {
   2048         case 'a': case 'A':
   2049             transformA(result);
   2050             break;
   2051         case 'e': case 'E':
   2052             transformE(result);
   2053             break;
   2054         case 'f':
   2055             transformF(result);
   2056             break;
   2057         case 'g':
   2058         case 'G':
   2059             transformG(result);
   2060             break;
   2061         default:
   2062             throw formatToken.unknownFormatConversionException();
   2063         }
   2064 
   2065         formatToken.setPrecision(FormatToken.UNSET);
   2066 
   2067         int startIndex = 0;
   2068         if (result.charAt(0) == localeData.minusSign) {
   2069             if (formatToken.flagParenthesis) {
   2070                 return wrapParentheses(result);
   2071             }
   2072         } else {
   2073             if (formatToken.flagSpace) {
   2074                 result.insert(0, ' ');
   2075                 startIndex++;
   2076             }
   2077             if (formatToken.flagPlus) {
   2078                 result.insert(0, '+');
   2079                 startIndex++;
   2080             }
   2081         }
   2082 
   2083         char firstChar = result.charAt(0);
   2084         if (formatToken.flagZero && (firstChar == '+' || firstChar == localeData.minusSign)) {
   2085             startIndex = 1;
   2086         }
   2087 
   2088         if (conversionType == 'a' || conversionType == 'A') {
   2089             startIndex += 2;
   2090         }
   2091         return padding(result, startIndex);
   2092     }
   2093 
   2094     private void transformE(StringBuilder result) {
   2095         // All zeros in this method are *pattern* characters, so no localization.
   2096         final int precision = formatToken.getPrecision();
   2097         String pattern = "0E+00";
   2098         if (precision > 0) {
   2099             StringBuilder sb = new StringBuilder("0.");
   2100             char[] zeros = new char[precision];
   2101             Arrays.fill(zeros, '0');
   2102             sb.append(zeros);
   2103             sb.append("E+00");
   2104             pattern = sb.toString();
   2105         }
   2106 
   2107         NativeDecimalFormat nf = getDecimalFormat(pattern);
   2108         char[] chars;
   2109         if (arg instanceof BigDecimal) {
   2110             chars = nf.formatBigDecimal((BigDecimal) arg, null);
   2111         } else {
   2112             chars = nf.formatDouble(((Number) arg).doubleValue(), null);
   2113         }
   2114         // Unlike %f, %e uses 'e' (regardless of what the DecimalFormatSymbols would have us use).
   2115         for (int i = 0; i < chars.length; ++i) {
   2116             if (chars[i] == 'E') {
   2117                 chars[i] = 'e';
   2118             }
   2119         }
   2120         result.append(chars);
   2121         // The # flag requires that we always output a decimal separator.
   2122         if (formatToken.flagSharp && precision == 0) {
   2123             int indexOfE = result.indexOf("e");
   2124             result.insert(indexOfE, localeData.decimalSeparator);
   2125         }
   2126     }
   2127 
   2128     private void transformG(StringBuilder result) {
   2129         int precision = formatToken.getPrecision();
   2130         if (precision == 0) {
   2131             precision = 1;
   2132         }
   2133         formatToken.setPrecision(precision);
   2134 
   2135         double d = ((Number) arg).doubleValue();
   2136         if (d == 0.0) {
   2137             precision--;
   2138             formatToken.setPrecision(precision);
   2139             transformF(result);
   2140             return;
   2141         }
   2142 
   2143         boolean requireScientificRepresentation = true;
   2144         d = Math.abs(d);
   2145         if (Double.isInfinite(d)) {
   2146             precision = formatToken.getPrecision();
   2147             precision--;
   2148             formatToken.setPrecision(precision);
   2149             transformE(result);
   2150             return;
   2151         }
   2152         BigDecimal b = new BigDecimal(d, new MathContext(precision));
   2153         d = b.doubleValue();
   2154         long l = b.longValue();
   2155 
   2156         if (d >= 1 && d < Math.pow(10, precision)) {
   2157             if (l < Math.pow(10, precision)) {
   2158                 requireScientificRepresentation = false;
   2159                 precision -= String.valueOf(l).length();
   2160                 precision = precision < 0 ? 0 : precision;
   2161                 l = Math.round(d * Math.pow(10, precision + 1));
   2162                 if (String.valueOf(l).length() <= formatToken.getPrecision()) {
   2163                     precision++;
   2164                 }
   2165                 formatToken.setPrecision(precision);
   2166             }
   2167         } else {
   2168             l = b.movePointRight(4).longValue();
   2169             if (d >= Math.pow(10, -4) && d < 1) {
   2170                 requireScientificRepresentation = false;
   2171                 precision += 4 - String.valueOf(l).length();
   2172                 l = b.movePointRight(precision + 1).longValue();
   2173                 if (String.valueOf(l).length() <= formatToken.getPrecision()) {
   2174                     precision++;
   2175                 }
   2176                 l = b.movePointRight(precision).longValue();
   2177                 if (l >= Math.pow(10, precision - 4)) {
   2178                     formatToken.setPrecision(precision);
   2179                 }
   2180             }
   2181         }
   2182         if (requireScientificRepresentation) {
   2183             precision = formatToken.getPrecision();
   2184             precision--;
   2185             formatToken.setPrecision(precision);
   2186             transformE(result);
   2187         } else {
   2188             transformF(result);
   2189         }
   2190     }
   2191 
   2192     private void transformF(StringBuilder result) {
   2193         // All zeros in this method are *pattern* characters, so no localization.
   2194         String pattern = "0.000000";
   2195         final int precision = formatToken.getPrecision();
   2196         if (formatToken.flagComma || precision != FormatToken.DEFAULT_PRECISION) {
   2197             StringBuilder patternBuilder = new StringBuilder();
   2198             if (formatToken.flagComma) {
   2199                 patternBuilder.append(',');
   2200                 int groupingSize = 3;
   2201                 char[] sharps = new char[groupingSize - 1];
   2202                 Arrays.fill(sharps, '#');
   2203                 patternBuilder.append(sharps);
   2204             }
   2205             patternBuilder.append('0');
   2206             if (precision > 0) {
   2207                 patternBuilder.append('.');
   2208                 for (int i = 0; i < precision; ++i) {
   2209                     patternBuilder.append('0');
   2210                 }
   2211             }
   2212             pattern = patternBuilder.toString();
   2213         }
   2214 
   2215         NativeDecimalFormat nf = getDecimalFormat(pattern);
   2216         if (arg instanceof BigDecimal) {
   2217             result.append(nf.formatBigDecimal((BigDecimal) arg, null));
   2218         } else {
   2219             result.append(nf.formatDouble(((Number) arg).doubleValue(), null));
   2220         }
   2221         // The # flag requires that we always output a decimal separator.
   2222         if (formatToken.flagSharp && precision == 0) {
   2223             result.append(localeData.decimalSeparator);
   2224         }
   2225     }
   2226 
   2227     private void transformA(StringBuilder result) {
   2228         if (arg instanceof Float) {
   2229             result.append(Float.toHexString(((Float) arg).floatValue()));
   2230         } else if (arg instanceof Double) {
   2231             result.append(Double.toHexString(((Double) arg).doubleValue()));
   2232         } else {
   2233             throw badArgumentType();
   2234         }
   2235 
   2236         if (!formatToken.isPrecisionSet()) {
   2237             return;
   2238         }
   2239 
   2240         int precision = formatToken.getPrecision();
   2241         if (precision == 0) {
   2242             precision = 1;
   2243         }
   2244         int indexOfFirstFractionalDigit = result.indexOf(".") + 1;
   2245         int indexOfP = result.indexOf("p");
   2246         int fractionalLength = indexOfP - indexOfFirstFractionalDigit;
   2247 
   2248         if (fractionalLength == precision) {
   2249             return;
   2250         }
   2251 
   2252         if (fractionalLength < precision) {
   2253             char[] zeros = new char[precision - fractionalLength];
   2254             Arrays.fill(zeros, '0'); // %a shouldn't be localized.
   2255             result.insert(indexOfP, zeros);
   2256             return;
   2257         }
   2258         result.delete(indexOfFirstFractionalDigit + precision, indexOfP);
   2259     }
   2260 
   2261     private static class FormatSpecifierParser {
   2262         private String format;
   2263         private int length;
   2264 
   2265         private int startIndex;
   2266         private int i;
   2267 
   2268         /**
   2269          * Constructs a new parser for the given format string.
   2270          */
   2271         FormatSpecifierParser(String format) {
   2272             this.format = format;
   2273             this.length = format.length();
   2274         }
   2275 
   2276         /**
   2277          * Returns a FormatToken representing the format specifier starting at 'offset'.
   2278          * @param offset the first character after the '%'
   2279          */
   2280         FormatToken parseFormatToken(int offset) {
   2281             this.startIndex = offset;
   2282             this.i = offset;
   2283             return parseArgumentIndexAndFlags(new FormatToken());
   2284         }
   2285 
   2286         /**
   2287          * Returns a string corresponding to the last format specifier that was parsed.
   2288          * Used to construct error messages.
   2289          */
   2290         String getFormatSpecifierText() {
   2291             return format.substring(startIndex, i);
   2292         }
   2293 
   2294         private int peek() {
   2295             return (i < length) ? format.charAt(i) : -1;
   2296         }
   2297 
   2298         private char advance() {
   2299             if (i >= length) {
   2300                 throw unknownFormatConversionException();
   2301             }
   2302             return format.charAt(i++);
   2303         }
   2304 
   2305         private UnknownFormatConversionException unknownFormatConversionException() {
   2306             throw new UnknownFormatConversionException(getFormatSpecifierText());
   2307         }
   2308 
   2309         private FormatToken parseArgumentIndexAndFlags(FormatToken token) {
   2310             // Parse the argument index, if there is one.
   2311             int position = i;
   2312             int ch = peek();
   2313             if (Character.isDigit(ch)) {
   2314                 int number = nextInt();
   2315                 if (peek() == '$') {
   2316                     // The number was an argument index.
   2317                     advance(); // Swallow the '$'.
   2318                     if (number == FormatToken.UNSET) {
   2319                         throw new MissingFormatArgumentException(getFormatSpecifierText());
   2320                     }
   2321                     // k$ stands for the argument whose index is k-1 except that
   2322                     // 0$ and 1$ both stand for the first element.
   2323                     token.setArgIndex(Math.max(0, number - 1));
   2324                 } else {
   2325                     if (ch == '0') {
   2326                         // The digit zero is a format flag, so reparse it as such.
   2327                         i = position;
   2328                     } else {
   2329                         // The number was a width. This means there are no flags to parse.
   2330                         return parseWidth(token, number);
   2331                     }
   2332                 }
   2333             } else if (ch == '<') {
   2334                 token.setArgIndex(FormatToken.LAST_ARGUMENT_INDEX);
   2335                 advance();
   2336             }
   2337 
   2338             // Parse the flags.
   2339             while (token.setFlag(peek())) {
   2340                 advance();
   2341             }
   2342 
   2343             // What comes next?
   2344             ch = peek();
   2345             if (Character.isDigit(ch)) {
   2346                 return parseWidth(token, nextInt());
   2347             } else if (ch == '.') {
   2348                 return parsePrecision(token);
   2349             } else {
   2350                 return parseConversionType(token);
   2351             }
   2352         }
   2353 
   2354         // We pass the width in because in some cases we've already parsed it.
   2355         // (Because of the ambiguity between argument indexes and widths.)
   2356         private FormatToken parseWidth(FormatToken token, int width) {
   2357             token.setWidth(width);
   2358             int ch = peek();
   2359             if (ch == '.') {
   2360                 return parsePrecision(token);
   2361             } else {
   2362                 return parseConversionType(token);
   2363             }
   2364         }
   2365 
   2366         private FormatToken parsePrecision(FormatToken token) {
   2367             advance(); // Swallow the '.'.
   2368             int ch = peek();
   2369             if (Character.isDigit(ch)) {
   2370                 token.setPrecision(nextInt());
   2371                 return parseConversionType(token);
   2372             } else {
   2373                 // The precision is required but not given by the format string.
   2374                 throw unknownFormatConversionException();
   2375             }
   2376         }
   2377 
   2378         private FormatToken parseConversionType(FormatToken token) {
   2379             char conversionType = advance(); // A conversion type is mandatory.
   2380             token.setConversionType(conversionType);
   2381             if (conversionType == 't' || conversionType == 'T') {
   2382                 char dateSuffix = advance(); // A date suffix is mandatory for 't' or 'T'.
   2383                 token.setDateSuffix(dateSuffix);
   2384             }
   2385             return token;
   2386         }
   2387 
   2388         // Parses an integer (of arbitrary length, but typically just one digit).
   2389         private int nextInt() {
   2390             long value = 0;
   2391             while (i < length && Character.isDigit(format.charAt(i))) {
   2392                 value = 10 * value + (format.charAt(i++) - '0');
   2393                 if (value > Integer.MAX_VALUE) {
   2394                     return failNextInt();
   2395                 }
   2396             }
   2397             return (int) value;
   2398         }
   2399 
   2400         // Swallow remaining digits to resync our attempted parse, but return failure.
   2401         private int failNextInt() {
   2402             while (Character.isDigit(peek())) {
   2403                 advance();
   2404             }
   2405             return FormatToken.UNSET;
   2406         }
   2407     }
   2408 }
   2409