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