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 %<d %<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 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 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 ١٢٣ 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(); 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