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 */
16package java.util;
17
18import java.io.BufferedWriter;
19import java.io.Closeable;
20import java.io.File;
21import java.io.FileNotFoundException;
22import java.io.FileOutputStream;
23import java.io.Flushable;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.io.OutputStreamWriter;
27import java.io.PrintStream;
28import java.io.UnsupportedEncodingException;
29import java.math.BigDecimal;
30import java.math.BigInteger;
31import java.math.MathContext;
32import java.nio.charset.Charset;
33import libcore.icu.LocaleData;
34import libcore.icu.NativeDecimalFormat;
35import 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>
215format("%f", 123.456f);
216format("%.1f", 123.456f);
217format("%1.5f", 123.456f);
218format("%10f", 123.456f);
219format("%6.0f", 123.456f);</td>
220 * <td width="30%" valign="top"><pre>
221123.456001
222123.5
223123.45600
224123.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>
231format("%e", 123.456f);
232format("%.1e", 123.456f);
233format("%1.5E", 123.456f);
234format("%10E", 123.456f);
235format("%6.0E", 123.456f);</td>
236 * <td width="30%" valign="top"><pre>
2371.234560e+02
2381.2e+02
2391.23456E+02
2401.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 */
526public 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("ps == null");
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