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