Path: blob/master/src/java.base/share/classes/java/util/Formatter.java
41152 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;2627import java.io.BufferedWriter;28import java.io.Closeable;29import java.io.IOException;30import java.io.File;31import java.io.FileOutputStream;32import java.io.FileNotFoundException;33import java.io.Flushable;34import java.io.OutputStream;35import java.io.OutputStreamWriter;36import java.io.PrintStream;37import java.io.UnsupportedEncodingException;38import java.math.BigDecimal;39import java.math.BigInteger;40import java.math.MathContext;41import java.math.RoundingMode;42import java.nio.charset.Charset;43import java.nio.charset.IllegalCharsetNameException;44import java.nio.charset.UnsupportedCharsetException;45import java.text.DateFormatSymbols;46import java.text.DecimalFormat;47import java.text.DecimalFormatSymbols;48import java.text.NumberFormat;49import java.text.spi.NumberFormatProvider;50import java.util.regex.Matcher;51import java.util.regex.Pattern;5253import java.time.DateTimeException;54import java.time.Instant;55import java.time.ZoneId;56import java.time.ZoneOffset;57import java.time.temporal.ChronoField;58import java.time.temporal.TemporalAccessor;59import java.time.temporal.TemporalQueries;60import java.time.temporal.UnsupportedTemporalTypeException;6162import jdk.internal.math.DoubleConsts;63import jdk.internal.math.FormattedFloatingDecimal;64import sun.util.locale.provider.LocaleProviderAdapter;65import sun.util.locale.provider.ResourceBundleBasedAdapter;6667/**68* An interpreter for printf-style format strings. This class provides support69* for layout justification and alignment, common formats for numeric, string,70* and date/time data, and locale-specific output. Common Java types such as71* {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}72* are supported. Limited formatting customization for arbitrary user types is73* provided through the {@link Formattable} interface.74*75* <p> Formatters are not necessarily safe for multithreaded access. Thread76* safety is optional and is the responsibility of users of methods in this77* class.78*79* <p> Formatted printing for the Java language is heavily inspired by C's80* {@code printf}. Although the format strings are similar to C, some81* customizations have been made to accommodate the Java language and exploit82* some of its features. Also, Java formatting is more strict than C's; for83* example, if a conversion is incompatible with a flag, an exception will be84* thrown. In C inapplicable flags are silently ignored. The format strings85* are thus intended to be recognizable to C programmers but not necessarily86* completely compatible with those in C.87*88* <p> Examples of expected usage:89*90* <blockquote><pre>91* StringBuilder sb = new StringBuilder();92* // Send all output to the Appendable object sb93* Formatter formatter = new Formatter(sb, Locale.US);94*95* // Explicit argument indices may be used to re-order output.96* formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d")97* // -> " d c b a"98*99* // Optional locale as the first argument can be used to get100* // locale-specific formatting of numbers. The precision and width can be101* // given to round and align the value.102* formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E);103* // -> "e = +2,7183"104*105* // The '(' numeric flag may be used to format negative numbers with106* // parentheses rather than a minus sign. Group separators are107* // automatically inserted.108* formatter.format("Amount gained or lost since last statement: $ %(,.2f",109* balanceDelta);110* // -> "Amount gained or lost since last statement: $ (6,217.58)"111* </pre></blockquote>112*113* <p> Convenience methods for common formatting requests exist as illustrated114* by the following invocations:115*116* <blockquote><pre>117* // Writes a formatted string to System.out.118* System.out.format("Local time: %tT", Calendar.getInstance());119* // -> "Local time: 13:34:18"120*121* // Writes formatted output to System.err.122* System.err.printf("Unable to open file '%1$s': %2$s",123* fileName, exception.getMessage());124* // -> "Unable to open file 'food': No such file or directory"125* </pre></blockquote>126*127* <p> Like C's {@code sprintf(3)}, Strings may be formatted using the static128* method {@link String#format(String,Object...) String.format}:129*130* <blockquote><pre>131* // Format a string containing a date.132* import java.util.Calendar;133* import java.util.GregorianCalendar;134* import static java.util.Calendar.*;135*136* Calendar c = new GregorianCalendar(1995, MAY, 23);137* String s = String.format("Duke's Birthday: %1$tb %1$te, %1$tY", c);138* // -> s == "Duke's Birthday: May 23, 1995"139* </pre></blockquote>140*141* <h2><a id="org">Organization</a></h2>142*143* <p> This specification is divided into two sections. The first section, <a144* href="#summary">Summary</a>, covers the basic formatting concepts. This145* section is intended for users who want to get started quickly and are146* familiar with formatted printing in other programming languages. The second147* section, <a href="#detail">Details</a>, covers the specific implementation148* details. It is intended for users who want more precise specification of149* formatting behavior.150*151* <h2><a id="summary">Summary</a></h2>152*153* <p> This section is intended to provide a brief overview of formatting154* concepts. For precise behavioral details, refer to the <a155* href="#detail">Details</a> section.156*157* <h3><a id="syntax">Format String Syntax</a></h3>158*159* <p> Every method which produces formatted output requires a <i>format160* string</i> and an <i>argument list</i>. The format string is a {@link161* String} which may contain fixed text and one or more embedded <i>format162* specifiers</i>. Consider the following example:163*164* <blockquote><pre>165* Calendar c = ...;166* String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c);167* </pre></blockquote>168*169* This format string is the first argument to the {@code format} method. It170* contains three format specifiers "{@code %1$tm}", "{@code %1$te}", and171* "{@code %1$tY}" which indicate how the arguments should be processed and172* where they should be inserted in the text. The remaining portions of the173* format string are fixed text including {@code "Dukes Birthday: "} and any174* other spaces or punctuation.175*176* The argument list consists of all arguments passed to the method after the177* format string. In the above example, the argument list is of size one and178* consists of the {@link java.util.Calendar Calendar} object {@code c}.179*180* <ul>181*182* <li> The format specifiers for general, character, and numeric types have183* the following syntax:184*185* <blockquote><pre>186* %[argument_index$][flags][width][.precision]conversion187* </pre></blockquote>188*189* <p> The optional <i>argument_index</i> is a decimal integer indicating the190* position of the argument in the argument list. The first argument is191* referenced by "{@code 1$}", the second by "{@code 2$}", etc.192*193* <p> The optional <i>flags</i> is a set of characters that modify the output194* format. The set of valid flags depends on the conversion.195*196* <p> The optional <i>width</i> is a positive decimal integer indicating197* the minimum number of characters to be written to the output.198*199* <p> The optional <i>precision</i> is a non-negative decimal integer usually200* used to restrict the number of characters. The specific behavior depends on201* the conversion.202*203* <p> The required <i>conversion</i> is a character indicating how the204* argument should be formatted. The set of valid conversions for a given205* argument depends on the argument's data type.206*207* <li> The format specifiers for types which are used to represents dates and208* times have the following syntax:209*210* <blockquote><pre>211* %[argument_index$][flags][width]conversion212* </pre></blockquote>213*214* <p> The optional <i>argument_index</i>, <i>flags</i> and <i>width</i> are215* defined as above.216*217* <p> The required <i>conversion</i> is a two character sequence. The first218* character is {@code 't'} or {@code 'T'}. The second character indicates219* the format to be used. These characters are similar to but not completely220* identical to those defined by GNU {@code date} and POSIX221* {@code strftime(3c)}.222*223* <li> The format specifiers which do not correspond to arguments have the224* following syntax:225*226* <blockquote><pre>227* %[flags][width]conversion228* </pre></blockquote>229*230* <p> The optional <i>flags</i> and <i>width</i> is defined as above.231*232* <p> The required <i>conversion</i> is a character indicating content to be233* inserted in the output.234*235* </ul>236*237* <h3> Conversions </h3>238*239* <p> Conversions are divided into the following categories:240*241* <ol>242*243* <li> <b>General</b> - may be applied to any argument244* type245*246* <li> <b>Character</b> - may be applied to basic types which represent247* Unicode characters: {@code char}, {@link Character}, {@code byte}, {@link248* Byte}, {@code short}, and {@link Short}. This conversion may also be249* applied to the types {@code int} and {@link Integer} when {@link250* Character#isValidCodePoint} returns {@code true}251*252* <li> <b>Numeric</b>253*254* <ol>255*256* <li> <b>Integral</b> - may be applied to Java integral types: {@code byte},257* {@link Byte}, {@code short}, {@link Short}, {@code int} and {@link258* Integer}, {@code long}, {@link Long}, and {@link java.math.BigInteger259* BigInteger} (but not {@code char} or {@link Character})260*261* <li><b>Floating Point</b> - may be applied to Java floating-point types:262* {@code float}, {@link Float}, {@code double}, {@link Double}, and {@link263* java.math.BigDecimal BigDecimal}264*265* </ol>266*267* <li> <b>Date/Time</b> - may be applied to Java types which are capable of268* encoding a date or time: {@code long}, {@link Long}, {@link Calendar},269* {@link Date} and {@link TemporalAccessor TemporalAccessor}270*271* <li> <b>Percent</b> - produces a literal {@code '%'}272* (<code>'\u0025'</code>)273*274* <li> <b>Line Separator</b> - produces the platform-specific line separator275*276* </ol>277*278* <p> For category <i>General</i>, <i>Character</i>, <i>Numeric</i>,279* <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,280* if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".281*282* <p> The following table summarizes the supported conversions. Conversions283* denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},284* {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},285* {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding286* lower-case conversion characters except that the result is converted to287* upper case according to the rules of the prevailing {@link java.util.Locale288* Locale}. If there is no explicit locale specified, either at the289* construction of the instance or as a parameter to its method290* invocation, then the {@link java.util.Locale.Category#FORMAT default locale}291* is used.292*293*294* <table class="striped">295* <caption style="display:none">genConv</caption>296* <thead>297* <tr><th scope="col" style="vertical-align:bottom"> Conversion298* <th scope="col" style="vertical-align:bottom"> Argument Category299* <th scope="col" style="vertical-align:bottom"> Description300* </thead>301* <tbody>302* <tr><th scope="row" style="vertical-align:top"> {@code 'b'}, {@code 'B'}303* <td style="vertical-align:top"> general304* <td> If the argument <i>arg</i> is {@code null}, then the result is305* "{@code false}". If <i>arg</i> is a {@code boolean} or {@link306* Boolean}, then the result is the string returned by {@link307* String#valueOf(boolean) String.valueOf(arg)}. Otherwise, the result is308* "true".309*310* <tr><th scope="row" style="vertical-align:top"> {@code 'h'}, {@code 'H'}311* <td style="vertical-align:top"> general312* <td> The result is obtained by invoking313* {@code Integer.toHexString(arg.hashCode())}.314*315* <tr><th scope="row" style="vertical-align:top"> {@code 's'}, {@code 'S'}316* <td style="vertical-align:top"> general317* <td> If <i>arg</i> implements {@link Formattable}, then318* {@link Formattable#formatTo arg.formatTo} is invoked. Otherwise, the319* result is obtained by invoking {@code arg.toString()}.320*321* <tr><th scope="row" style="vertical-align:top">{@code 'c'}, {@code 'C'}322* <td style="vertical-align:top"> character323* <td> The result is a Unicode character324*325* <tr><th scope="row" style="vertical-align:top">{@code 'd'}326* <td style="vertical-align:top"> integral327* <td> The result is formatted as a decimal integer328*329* <tr><th scope="row" style="vertical-align:top">{@code 'o'}330* <td style="vertical-align:top"> integral331* <td> The result is formatted as an octal integer332*333* <tr><th scope="row" style="vertical-align:top">{@code 'x'}, {@code 'X'}334* <td style="vertical-align:top"> integral335* <td> The result is formatted as a hexadecimal integer336*337* <tr><th scope="row" style="vertical-align:top">{@code 'e'}, {@code 'E'}338* <td style="vertical-align:top"> floating point339* <td> The result is formatted as a decimal number in computerized340* scientific notation341*342* <tr><th scope="row" style="vertical-align:top">{@code 'f'}343* <td style="vertical-align:top"> floating point344* <td> The result is formatted as a decimal number345*346* <tr><th scope="row" style="vertical-align:top">{@code 'g'}, {@code 'G'}347* <td style="vertical-align:top"> floating point348* <td> The result is formatted using computerized scientific notation or349* decimal format, depending on the precision and the value after rounding.350*351* <tr><th scope="row" style="vertical-align:top">{@code 'a'}, {@code 'A'}352* <td style="vertical-align:top"> floating point353* <td> The result is formatted as a hexadecimal floating-point number with354* a significand and an exponent. This conversion is <b>not</b> supported355* for the {@code BigDecimal} type despite the latter's being in the356* <i>floating point</i> argument category.357*358* <tr><th scope="row" style="vertical-align:top">{@code 't'}, {@code 'T'}359* <td style="vertical-align:top"> date/time360* <td> Prefix for date and time conversion characters. See <a361* href="#dt">Date/Time Conversions</a>.362*363* <tr><th scope="row" style="vertical-align:top">{@code '%'}364* <td style="vertical-align:top"> percent365* <td> The result is a literal {@code '%'} (<code>'\u0025'</code>)366*367* <tr><th scope="row" style="vertical-align:top">{@code 'n'}368* <td style="vertical-align:top"> line separator369* <td> The result is the platform-specific line separator370*371* </tbody>372* </table>373*374* <p> Any characters not explicitly defined as conversions are illegal and are375* reserved for future extensions.376*377* <h3><a id="dt">Date/Time Conversions</a></h3>378*379* <p> The following date and time conversion suffix characters are defined for380* the {@code 't'} and {@code 'T'} conversions. The types are similar to but381* not completely identical to those defined by GNU {@code date} and POSIX382* {@code strftime(3c)}. Additional conversion types are provided to access383* Java-specific functionality (e.g. {@code 'L'} for milliseconds within the384* second).385*386* <p> The following conversion characters are used for formatting times:387*388* <table class="striped">389* <caption style="display:none">time</caption>390* <tbody>391* <tr><th scope="row" style="vertical-align:top"> {@code 'H'}392* <td> Hour of the day for the 24-hour clock, formatted as two digits with393* a leading zero as necessary i.e. {@code 00 - 23}.394*395* <tr><th scope="row" style="vertical-align:top">{@code 'I'}396* <td> Hour for the 12-hour clock, formatted as two digits with a leading397* zero as necessary, i.e. {@code 01 - 12}.398*399* <tr><th scope="row" style="vertical-align:top">{@code 'k'}400* <td> Hour of the day for the 24-hour clock, i.e. {@code 0 - 23}.401*402* <tr><th scope="row" style="vertical-align:top">{@code 'l'}403* <td> Hour for the 12-hour clock, i.e. {@code 1 - 12}.404*405* <tr><th scope="row" style="vertical-align:top">{@code 'M'}406* <td> Minute within the hour formatted as two digits with a leading zero407* as necessary, i.e. {@code 00 - 59}.408*409* <tr><th scope="row" style="vertical-align:top">{@code 'S'}410* <td> Seconds within the minute, formatted as two digits with a leading411* zero as necessary, i.e. {@code 00 - 60} ("{@code 60}" is a special412* value required to support leap seconds).413*414* <tr><th scope="row" style="vertical-align:top">{@code 'L'}415* <td> Millisecond within the second formatted as three digits with416* leading zeros as necessary, i.e. {@code 000 - 999}.417*418* <tr><th scope="row" style="vertical-align:top">{@code 'N'}419* <td> Nanosecond within the second, formatted as nine digits with leading420* zeros as necessary, i.e. {@code 000000000 - 999999999}.421*422* <tr><th scope="row" style="vertical-align:top">{@code 'p'}423* <td> Locale-specific {@linkplain424* java.text.DateFormatSymbols#getAmPmStrings morning or afternoon} marker425* in lower case, e.g."{@code am}" or "{@code pm}". Use of the conversion426* prefix {@code 'T'} forces this output to upper case.427*428* <tr><th scope="row" style="vertical-align:top">{@code 'z'}429* <td> <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC 822</a>430* style numeric time zone offset from GMT, e.g. {@code -0800}. This431* value will be adjusted as necessary for Daylight Saving Time. For432* {@code long}, {@link Long}, and {@link Date} the time zone used is433* the {@linkplain TimeZone#getDefault() default time zone} for this434* instance of the Java virtual machine.435*436* <tr><th scope="row" style="vertical-align:top">{@code 'Z'}437* <td> A string representing the abbreviation for the time zone. This438* value will be adjusted as necessary for Daylight Saving Time. For439* {@code long}, {@link Long}, and {@link Date} the time zone used is440* the {@linkplain TimeZone#getDefault() default time zone} for this441* instance of the Java virtual machine. The Formatter's locale will442* supersede the locale of the argument (if any).443*444* <tr><th scope="row" style="vertical-align:top">{@code 's'}445* <td> Seconds since the beginning of the epoch starting at 1 January 1970446* {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE/1000} to447* {@code Long.MAX_VALUE/1000}.448*449* <tr><th scope="row" style="vertical-align:top">{@code 'Q'}450* <td> Milliseconds since the beginning of the epoch starting at 1 January451* 1970 {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE} to452* {@code Long.MAX_VALUE}.453*454* </tbody>455* </table>456*457* <p> The following conversion characters are used for formatting dates:458*459* <table class="striped">460* <caption style="display:none">date</caption>461* <tbody>462*463* <tr><th scope="row" style="vertical-align:top">{@code 'B'}464* <td> Locale-specific {@linkplain java.text.DateFormatSymbols#getMonths465* full month name}, e.g. {@code "January"}, {@code "February"}.466*467* <tr><th scope="row" style="vertical-align:top">{@code 'b'}468* <td> Locale-specific {@linkplain469* java.text.DateFormatSymbols#getShortMonths abbreviated month name},470* e.g. {@code "Jan"}, {@code "Feb"}.471*472* <tr><th scope="row" style="vertical-align:top">{@code 'h'}473* <td> Same as {@code 'b'}.474*475* <tr><th scope="row" style="vertical-align:top">{@code 'A'}476* <td> Locale-specific full name of the {@linkplain477* java.text.DateFormatSymbols#getWeekdays day of the week},478* e.g. {@code "Sunday"}, {@code "Monday"}479*480* <tr><th scope="row" style="vertical-align:top">{@code 'a'}481* <td> Locale-specific short name of the {@linkplain482* java.text.DateFormatSymbols#getShortWeekdays day of the week},483* e.g. {@code "Sun"}, {@code "Mon"}484*485* <tr><th scope="row" style="vertical-align:top">{@code 'C'}486* <td> Four-digit year divided by {@code 100}, formatted as two digits487* with leading zero as necessary, i.e. {@code 00 - 99}488*489* <tr><th scope="row" style="vertical-align:top">{@code 'Y'}490* <td> Year, formatted as at least four digits with leading zeros as491* necessary, e.g. {@code 0092} equals {@code 92} CE for the Gregorian492* calendar.493*494* <tr><th scope="row" style="vertical-align:top">{@code 'y'}495* <td> Last two digits of the year, formatted with leading zeros as496* necessary, i.e. {@code 00 - 99}.497*498* <tr><th scope="row" style="vertical-align:top">{@code 'j'}499* <td> Day of year, formatted as three digits with leading zeros as500* necessary, e.g. {@code 001 - 366} for the Gregorian calendar.501*502* <tr><th scope="row" style="vertical-align:top">{@code 'm'}503* <td> Month, formatted as two digits with leading zeros as necessary,504* i.e. {@code 01 - 13}.505*506* <tr><th scope="row" style="vertical-align:top">{@code 'd'}507* <td> Day of month, formatted as two digits with leading zeros as508* necessary, i.e. {@code 01 - 31}509*510* <tr><th scope="row" style="vertical-align:top">{@code 'e'}511* <td> Day of month, formatted as two digits, i.e. {@code 1 - 31}.512*513* </tbody>514* </table>515*516* <p> The following conversion characters are used for formatting common517* date/time compositions.518*519* <table class="striped">520* <caption style="display:none">composites</caption>521* <tbody>522*523* <tr><th scope="row" style="vertical-align:top">{@code 'R'}524* <td> Time formatted for the 24-hour clock as {@code "%tH:%tM"}525*526* <tr><th scope="row" style="vertical-align:top">{@code 'T'}527* <td> Time formatted for the 24-hour clock as {@code "%tH:%tM:%tS"}.528*529* <tr><th scope="row" style="vertical-align:top">{@code 'r'}530* <td> Time formatted for the 12-hour clock as {@code "%tI:%tM:%tS %Tp"}.531* The location of the morning or afternoon marker ({@code '%Tp'}) may be532* locale-dependent.533*534* <tr><th scope="row" style="vertical-align:top">{@code 'D'}535* <td> Date formatted as {@code "%tm/%td/%ty"}.536*537* <tr><th scope="row" style="vertical-align:top">{@code 'F'}538* <td> <a href="http://www.w3.org/TR/NOTE-datetime">ISO 8601</a>539* complete date formatted as {@code "%tY-%tm-%td"}.540*541* <tr><th scope="row" style="vertical-align:top">{@code 'c'}542* <td> Date and time formatted as {@code "%ta %tb %td %tT %tZ %tY"},543* e.g. {@code "Sun Jul 20 16:17:00 EDT 1969"}.544*545* </tbody>546* </table>547*548* <p> Any characters not explicitly defined as date/time conversion suffixes549* are illegal and are reserved for future extensions.550*551* <h3> Flags </h3>552*553* <p> The following table summarizes the supported flags. <i>y</i> means the554* flag is supported for the indicated argument types.555*556* <table class="striped">557* <caption style="display:none">genConv</caption>558* <thead>559* <tr><th scope="col" style="vertical-align:bottom"> Flag <th scope="col" style="vertical-align:bottom"> General560* <th scope="col" style="vertical-align:bottom"> Character <th scope="col" style="vertical-align:bottom"> Integral561* <th scope="col" style="vertical-align:bottom"> Floating Point562* <th scope="col" style="vertical-align:bottom"> Date/Time563* <th scope="col" style="vertical-align:bottom"> Description564* </thead>565* <tbody>566* <tr><th scope="row"> '-' <td style="text-align:center; vertical-align:top"> y567* <td style="text-align:center; vertical-align:top"> y568* <td style="text-align:center; vertical-align:top"> y569* <td style="text-align:center; vertical-align:top"> y570* <td style="text-align:center; vertical-align:top"> y571* <td> The result will be left-justified.572*573* <tr><th scope="row"> '#' <td style="text-align:center; vertical-align:top"> y<sup>1</sup>574* <td style="text-align:center; vertical-align:top"> -575* <td style="text-align:center; vertical-align:top"> y<sup>3</sup>576* <td style="text-align:center; vertical-align:top"> y577* <td style="text-align:center; vertical-align:top"> -578* <td> The result should use a conversion-dependent alternate form579*580* <tr><th scope="row"> '+' <td style="text-align:center; vertical-align:top"> -581* <td style="text-align:center; vertical-align:top"> -582* <td style="text-align:center; vertical-align:top"> y<sup>4</sup>583* <td style="text-align:center; vertical-align:top"> y584* <td style="text-align:center; vertical-align:top"> -585* <td> The result will always include a sign586*587* <tr><th scope="row"> ' ' <td style="text-align:center; vertical-align:top"> -588* <td style="text-align:center; vertical-align:top"> -589* <td style="text-align:center; vertical-align:top"> y<sup>4</sup>590* <td style="text-align:center; vertical-align:top"> y591* <td style="text-align:center; vertical-align:top"> -592* <td> The result will include a leading space for positive values593*594* <tr><th scope="row"> '0' <td style="text-align:center; vertical-align:top"> -595* <td style="text-align:center; vertical-align:top"> -596* <td style="text-align:center; vertical-align:top"> y597* <td style="text-align:center; vertical-align:top"> y598* <td style="text-align:center; vertical-align:top"> -599* <td> The result will be zero-padded600*601* <tr><th scope="row"> ',' <td style="text-align:center; vertical-align:top"> -602* <td style="text-align:center; vertical-align:top"> -603* <td style="text-align:center; vertical-align:top"> y<sup>2</sup>604* <td style="text-align:center; vertical-align:top"> y<sup>5</sup>605* <td style="text-align:center; vertical-align:top"> -606* <td> The result will include locale-specific {@linkplain607* java.text.DecimalFormatSymbols#getGroupingSeparator grouping separators}608*609* <tr><th scope="row"> '(' <td style="text-align:center; vertical-align:top"> -610* <td style="text-align:center; vertical-align:top"> -611* <td style="text-align:center; vertical-align:top"> y<sup>4</sup>612* <td style="text-align:center; vertical-align:top"> y<sup>5</sup>613* <td style="text-align:center"> -614* <td> The result will enclose negative numbers in parentheses615*616* </tbody>617* </table>618*619* <p> <sup>1</sup> Depends on the definition of {@link Formattable}.620*621* <p> <sup>2</sup> For {@code 'd'} conversion only.622*623* <p> <sup>3</sup> For {@code 'o'}, {@code 'x'}, and {@code 'X'}624* conversions only.625*626* <p> <sup>4</sup> For {@code 'd'}, {@code 'o'}, {@code 'x'}, and627* {@code 'X'} conversions applied to {@link java.math.BigInteger BigInteger}628* or {@code 'd'} applied to {@code byte}, {@link Byte}, {@code short}, {@link629* Short}, {@code int} and {@link Integer}, {@code long}, and {@link Long}.630*631* <p> <sup>5</sup> For {@code 'e'}, {@code 'E'}, {@code 'f'},632* {@code 'g'}, and {@code 'G'} conversions only.633*634* <p> Any characters not explicitly defined as flags are illegal and are635* reserved for future extensions.636*637* <h3> Width </h3>638*639* <p> The width is the minimum number of characters to be written to the640* output. For the line separator conversion, width is not applicable; if it641* is provided, an exception will be thrown.642*643* <h3> Precision </h3>644*645* <p> For general argument types, the precision is the maximum number of646* characters to be written to the output.647*648* <p> For the floating-point conversions {@code 'a'}, {@code 'A'}, {@code 'e'},649* {@code 'E'}, and {@code 'f'} the precision is the number of digits after the650* radix point. If the conversion is {@code 'g'} or {@code 'G'}, then the651* precision is the total number of digits in the resulting magnitude after652* rounding.653*654* <p> For character, integral, and date/time argument types and the percent655* and line separator conversions, the precision is not applicable; if a656* precision is provided, an exception will be thrown.657*658* <h3> Argument Index </h3>659*660* <p> The argument index is a decimal integer indicating the position of the661* argument in the argument list. The first argument is referenced by662* "{@code 1$}", the second by "{@code 2$}", etc.663*664* <p> Another way to reference arguments by position is to use the665* {@code '<'} (<code>'\u003c'</code>) flag, which causes the argument for666* the previous format specifier to be re-used. For example, the following two667* statements would produce identical strings:668*669* <blockquote><pre>670* Calendar c = ...;671* String s1 = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c);672*673* String s2 = String.format("Duke's Birthday: %1$tm %<te,%<tY", c);674* </pre></blockquote>675*676* <hr>677* <h2><a id="detail">Details</a></h2>678*679* <p> This section is intended to provide behavioral details for formatting,680* including conditions and exceptions, supported data types, localization, and681* interactions between flags, conversions, and data types. For an overview of682* formatting concepts, refer to the <a href="#summary">Summary</a>683*684* <p> Any characters not explicitly defined as conversions, date/time685* conversion suffixes, or flags are illegal and are reserved for686* future extensions. Use of such a character in a format string will687* cause an {@link UnknownFormatConversionException} or {@link688* UnknownFormatFlagsException} to be thrown.689*690* <p> If the format specifier contains a width or precision with an invalid691* value or which is otherwise unsupported, then a {@link692* IllegalFormatWidthException} or {@link IllegalFormatPrecisionException}693* respectively will be thrown. Similarly, values of zero for an argument694* index will result in an {@link IllegalFormatException}.695*696* <p> If a format specifier contains a conversion character that is not697* applicable to the corresponding argument, then an {@link698* IllegalFormatConversionException} will be thrown.699*700* <p> Values of <i>precision</i> must be in the range zero to701* {@link Integer#MAX_VALUE}, inclusive, otherwise702* {@link IllegalFormatPrecisionException} is thrown.</p>703*704* <p> Values of <i>width</i> must be in the range one to705* {@link Integer#MAX_VALUE}, inclusive, otherwise706* {@link IllegalFormatWidthException} will be thrown707* Note that widths can appear to have a negative value, but the negative sign708* is a <i>flag</i>. For example in the format string {@code "%-20s"} the709* <i>width</i> is <i>20</i> and the <i>flag</i> is "-".</p>710*711* <p> Values of <i>index</i> must be in the range one to712* {@link Integer#MAX_VALUE}, inclusive, otherwise713* {@link IllegalFormatException} will be thrown.</p>714*715* <p> All specified exceptions may be thrown by any of the {@code format}716* methods of {@code Formatter} as well as by any {@code format} convenience717* methods such as {@link String#format(String,Object...) String.format} and718* {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.719*720* <p> For category <i>General</i>, <i>Character</i>, <i>Numeric</i>,721* <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,722* if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".723*724* <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},725* {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},726* {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the727* corresponding lower-case conversion characters except that the result is728* converted to upper case according to the rules of the prevailing {@link729* java.util.Locale Locale}. If there is no explicit locale specified,730* either at the construction of the instance or as a parameter to its method731* invocation, then the {@link java.util.Locale.Category#FORMAT default locale}732* is used.733*734* <h3><a id="dgen">General</a></h3>735*736* <p> The following general conversions may be applied to any argument type:737*738* <table class="striped">739* <caption style="display:none">dgConv</caption>740* <tbody>741*742* <tr><th scope="row" style="vertical-align:top"> {@code 'b'}743* <td style="vertical-align:top"> <code>'\u0062'</code>744* <td> Produces either "{@code true}" or "{@code false}" as returned by745* {@link Boolean#toString(boolean)}.746*747* <p> If the argument is {@code null}, then the result is748* "{@code false}". If the argument is a {@code boolean} or {@link749* Boolean}, then the result is the string returned by {@link750* String#valueOf(boolean) String.valueOf()}. Otherwise, the result is751* "{@code true}".752*753* <p> If the {@code '#'} flag is given, then a {@link754* FormatFlagsConversionMismatchException} will be thrown.755*756* <tr><th scope="row" style="vertical-align:top"> {@code 'B'}757* <td style="vertical-align:top"> <code>'\u0042'</code>758* <td> The upper-case variant of {@code 'b'}.759*760* <tr><th scope="row" style="vertical-align:top"> {@code 'h'}761* <td style="vertical-align:top"> <code>'\u0068'</code>762* <td> Produces a string representing the hash code value of the object.763*764* <p> The result is obtained by invoking765* {@code Integer.toHexString(arg.hashCode())}.766*767* <p> If the {@code '#'} flag is given, then a {@link768* FormatFlagsConversionMismatchException} will be thrown.769*770* <tr><th scope="row" style="vertical-align:top"> {@code 'H'}771* <td style="vertical-align:top"> <code>'\u0048'</code>772* <td> The upper-case variant of {@code 'h'}.773*774* <tr><th scope="row" style="vertical-align:top"> {@code 's'}775* <td style="vertical-align:top"> <code>'\u0073'</code>776* <td> Produces a string.777*778* <p> If the argument implements {@link Formattable}, then779* its {@link Formattable#formatTo formatTo} method is invoked.780* Otherwise, the result is obtained by invoking the argument's781* {@code toString()} method.782*783* <p> If the {@code '#'} flag is given and the argument is not a {@link784* Formattable}, then a {@link FormatFlagsConversionMismatchException}785* will be thrown.786*787* <tr><th scope="row" style="vertical-align:top"> {@code 'S'}788* <td style="vertical-align:top"> <code>'\u0053'</code>789* <td> The upper-case variant of {@code 's'}.790*791* </tbody>792* </table>793*794* <p> The following <a id="dFlags">flags</a> apply to general conversions:795*796* <table class="striped">797* <caption style="display:none">dFlags</caption>798* <tbody>799*800* <tr><th scope="row" style="vertical-align:top"> {@code '-'}801* <td style="vertical-align:top"> <code>'\u002d'</code>802* <td> Left justifies the output. Spaces (<code>'\u0020'</code>) will be803* added at the end of the converted value as required to fill the minimum804* width of the field. If the width is not provided, then a {@link805* MissingFormatWidthException} will be thrown. If this flag is not given806* then the output will be right-justified.807*808* <tr><th scope="row" style="vertical-align:top"> {@code '#'}809* <td style="vertical-align:top"> <code>'\u0023'</code>810* <td> Requires the output use an alternate form. The definition of the811* form is specified by the conversion.812*813* </tbody>814* </table>815*816* <p> The <a id="genWidth">width</a> is the minimum number of characters to817* be written to the818* output. If the length of the converted value is less than the width then819* the output will be padded by <code>' '</code> (<code>'\u0020'</code>)820* until the total number of characters equals the width. The padding is on821* the left by default. If the {@code '-'} flag is given, then the padding822* will be on the right. If the width is not specified then there is no823* minimum.824*825* <p> The precision is the maximum number of characters to be written to the826* output. The precision is applied before the width, thus the output will be827* truncated to {@code precision} characters even if the width is greater than828* the precision. If the precision is not specified then there is no explicit829* limit on the number of characters.830*831* <h3><a id="dchar">Character</a></h3>832*833* This conversion may be applied to {@code char} and {@link Character}. It834* may also be applied to the types {@code byte}, {@link Byte},835* {@code short}, and {@link Short}, {@code int} and {@link Integer} when836* {@link Character#isValidCodePoint} returns {@code true}. If it returns837* {@code false} then an {@link IllegalFormatCodePointException} will be838* thrown.839*840* <table class="striped">841* <caption style="display:none">charConv</caption>842* <tbody>843*844* <tr><th scope="row" style="vertical-align:top"> {@code 'c'}845* <td style="vertical-align:top"> <code>'\u0063'</code>846* <td> Formats the argument as a Unicode character as described in <a847* href="../lang/Character.html#unicode">Unicode Character848* Representation</a>. This may be more than one 16-bit {@code char} in849* the case where the argument represents a supplementary character.850*851* <p> If the {@code '#'} flag is given, then a {@link852* FormatFlagsConversionMismatchException} will be thrown.853*854* <tr><th scope="row" style="vertical-align:top"> {@code 'C'}855* <td style="vertical-align:top"> <code>'\u0043'</code>856* <td> The upper-case variant of {@code 'c'}.857*858* </tbody>859* </table>860*861* <p> The {@code '-'} flag defined for <a href="#dFlags">General862* conversions</a> applies. If the {@code '#'} flag is given, then a {@link863* FormatFlagsConversionMismatchException} will be thrown.864*865* <p> The width is defined as for <a href="#genWidth">General conversions</a>.866*867* <p> The precision is not applicable. If the precision is specified then an868* {@link IllegalFormatPrecisionException} will be thrown.869*870* <h3><a id="dnum">Numeric</a></h3>871*872* <p> Numeric conversions are divided into the following categories:873*874* <ol>875*876* <li> <a href="#dnint"><b>Byte, Short, Integer, and Long</b></a>877*878* <li> <a href="#dnbint"><b>BigInteger</b></a>879*880* <li> <a href="#dndec"><b>Float and Double</b></a>881*882* <li> <a href="#dnbdec"><b>BigDecimal</b></a>883*884* </ol>885*886* <p> Numeric types will be formatted according to the following algorithm:887*888* <p><b><a id="L10nAlgorithm"> Number Localization Algorithm</a></b>889*890* <p> After digits are obtained for the integer part, fractional part, and891* exponent (as appropriate for the data type), the following transformation892* is applied:893*894* <ol>895*896* <li> Each digit character <i>d</i> in the string is replaced by a897* locale-specific digit computed relative to the current locale's898* {@linkplain java.text.DecimalFormatSymbols#getZeroDigit() zero digit}899* <i>z</i>; that is <i>d - </i> {@code '0'}900* <i> + z</i>.901*902* <li> If a decimal separator is present, a locale-specific {@linkplain903* java.text.DecimalFormatSymbols#getDecimalSeparator decimal separator} is904* substituted.905*906* <li> If the {@code ','} (<code>'\u002c'</code>)907* <a id="L10nGroup">flag</a> is given, then the locale-specific {@linkplain908* java.text.DecimalFormatSymbols#getGroupingSeparator grouping separator} is909* inserted by scanning the integer part of the string from least significant910* to most significant digits and inserting a separator at intervals defined by911* the locale's {@linkplain java.text.DecimalFormat#getGroupingSize() grouping912* size}.913*914* <li> If the {@code '0'} flag is given, then the locale-specific {@linkplain915* java.text.DecimalFormatSymbols#getZeroDigit() zero digits} are inserted916* after the sign character, if any, and before the first non-zero digit, until917* the length of the string is equal to the requested field width.918*919* <li> If the value is negative and the {@code '('} flag is given, then a920* {@code '('} (<code>'\u0028'</code>) is prepended and a {@code ')'}921* (<code>'\u0029'</code>) is appended.922*923* <li> If the value is negative (or floating-point negative zero) and924* {@code '('} flag is not given, then a {@code '-'} (<code>'\u002d'</code>)925* is prepended.926*927* <li> If the {@code '+'} flag is given and the value is positive or zero (or928* floating-point positive zero), then a {@code '+'} (<code>'\u002b'</code>)929* will be prepended.930*931* </ol>932*933* <p> If the value is NaN or positive infinity the literal strings "NaN" or934* "Infinity" respectively, will be output. If the value is negative infinity,935* then the output will be "(Infinity)" if the {@code '('} flag is given936* otherwise the output will be "-Infinity". These values are not localized.937*938* <p><a id="dnint"><b> Byte, Short, Integer, and Long </b></a>939*940* <p> The following conversions may be applied to {@code byte}, {@link Byte},941* {@code short}, {@link Short}, {@code int} and {@link Integer},942* {@code long}, and {@link Long}.943*944* <table class="striped">945* <caption style="display:none">IntConv</caption>946* <tbody>947*948* <tr><th scope="row" style="vertical-align:top"> {@code 'd'}949* <td style="vertical-align:top"> <code>'\u0064'</code>950* <td> Formats the argument as a decimal integer. The <a951* href="#L10nAlgorithm">localization algorithm</a> is applied.952*953* <p> If the {@code '0'} flag is given and the value is negative, then954* the zero padding will occur after the sign.955*956* <p> If the {@code '#'} flag is given then a {@link957* FormatFlagsConversionMismatchException} will be thrown.958*959* <tr><th scope="row" style="vertical-align:top"> {@code 'o'}960* <td style="vertical-align:top"> <code>'\u006f'</code>961* <td> Formats the argument as an integer in base eight. No localization962* is applied.963*964* <p> If <i>x</i> is negative then the result will be an unsigned value965* generated by adding 2<sup>n</sup> to the value where {@code n} is the966* number of bits in the type as returned by the static {@code SIZE} field967* in the {@linkplain Byte#SIZE Byte}, {@linkplain Short#SIZE Short},968* {@linkplain Integer#SIZE Integer}, or {@linkplain Long#SIZE Long}969* classes as appropriate.970*971* <p> If the {@code '#'} flag is given then the output will always begin972* with the radix indicator {@code '0'}.973*974* <p> If the {@code '0'} flag is given then the output will be padded975* with leading zeros to the field width following any indication of sign.976*977* <p> If {@code '('}, {@code '+'}, ' ', or {@code ','} flags978* are given then a {@link FormatFlagsConversionMismatchException} will be979* thrown.980*981* <tr><th scope="row" style="vertical-align:top"> {@code 'x'}982* <td style="vertical-align:top"> <code>'\u0078'</code>983* <td> Formats the argument as an integer in base sixteen. No984* localization is applied.985*986* <p> If <i>x</i> is negative then the result will be an unsigned value987* generated by adding 2<sup>n</sup> to the value where {@code n} is the988* number of bits in the type as returned by the static {@code SIZE} field989* in the {@linkplain Byte#SIZE Byte}, {@linkplain Short#SIZE Short},990* {@linkplain Integer#SIZE Integer}, or {@linkplain Long#SIZE Long}991* classes as appropriate.992*993* <p> If the {@code '#'} flag is given then the output will always begin994* with the radix indicator {@code "0x"}.995*996* <p> If the {@code '0'} flag is given then the output will be padded to997* the field width with leading zeros after the radix indicator or sign (if998* present).999*1000* <p> If {@code '('}, <code>' '</code>, {@code '+'}, or1001* {@code ','} flags are given then a {@link1002* FormatFlagsConversionMismatchException} will be thrown.1003*1004* <tr><th scope="row" style="vertical-align:top"> {@code 'X'}1005* <td style="vertical-align:top"> <code>'\u0058'</code>1006* <td> The upper-case variant of {@code 'x'}. The entire string1007* representing the number will be converted to {@linkplain1008* String#toUpperCase upper case} including the {@code 'x'} (if any) and1009* all hexadecimal digits {@code 'a'} - {@code 'f'}1010* (<code>'\u0061'</code> - <code>'\u0066'</code>).1011*1012* </tbody>1013* </table>1014*1015* <p> If the conversion is {@code 'o'}, {@code 'x'}, or {@code 'X'} and1016* both the {@code '#'} and the {@code '0'} flags are given, then result will1017* contain the radix indicator ({@code '0'} for octal and {@code "0x"} or1018* {@code "0X"} for hexadecimal), some number of zeros (based on the width),1019* and the value.1020*1021* <p> If the {@code '-'} flag is not given, then the space padding will occur1022* before the sign.1023*1024* <p> The following <a id="intFlags">flags</a> apply to numeric integral1025* conversions:1026*1027* <table class="striped">1028* <caption style="display:none">intFlags</caption>1029* <tbody>1030*1031* <tr><th scope="row" style="vertical-align:top"> {@code '+'}1032* <td style="vertical-align:top"> <code>'\u002b'</code>1033* <td> Requires the output to include a positive sign for all positive1034* numbers. If this flag is not given then only negative values will1035* include a sign.1036*1037* <p> If both the {@code '+'} and <code>' '</code> flags are given1038* then an {@link IllegalFormatFlagsException} will be thrown.1039*1040* <tr><th scope="row" style="vertical-align:top"> <code>' '</code>1041* <td style="vertical-align:top"> <code>'\u0020'</code>1042* <td> Requires the output to include a single extra space1043* (<code>'\u0020'</code>) for non-negative values.1044*1045* <p> If both the {@code '+'} and <code>' '</code> flags are given1046* then an {@link IllegalFormatFlagsException} will be thrown.1047*1048* <tr><th scope="row" style="vertical-align:top"> {@code '0'}1049* <td style="vertical-align:top"> <code>'\u0030'</code>1050* <td> Requires the output to be padded with leading {@linkplain1051* java.text.DecimalFormatSymbols#getZeroDigit zeros} to the minimum field1052* width following any sign or radix indicator except when converting NaN1053* or infinity. If the width is not provided, then a {@link1054* MissingFormatWidthException} will be thrown.1055*1056* <p> If both the {@code '-'} and {@code '0'} flags are given then an1057* {@link IllegalFormatFlagsException} will be thrown.1058*1059* <tr><th scope="row" style="vertical-align:top"> {@code ','}1060* <td style="vertical-align:top"> <code>'\u002c'</code>1061* <td> Requires the output to include the locale-specific {@linkplain1062* java.text.DecimalFormatSymbols#getGroupingSeparator group separators} as1063* described in the <a href="#L10nGroup">"group" section</a> of the1064* localization algorithm.1065*1066* <tr><th scope="row" style="vertical-align:top"> {@code '('}1067* <td style="vertical-align:top"> <code>'\u0028'</code>1068* <td> Requires the output to prepend a {@code '('}1069* (<code>'\u0028'</code>) and append a {@code ')'}1070* (<code>'\u0029'</code>) to negative values.1071*1072* </tbody>1073* </table>1074*1075* <p> If no <a id="intdFlags">flags</a> are given the default formatting is1076* as follows:1077*1078* <ul>1079*1080* <li> The output is right-justified within the {@code width}1081*1082* <li> Negative numbers begin with a {@code '-'} (<code>'\u002d'</code>)1083*1084* <li> Positive numbers and zero do not include a sign or extra leading1085* space1086*1087* <li> No grouping separators are included1088*1089* </ul>1090*1091* <p> The <a id="intWidth">width</a> is the minimum number of characters to1092* be written to the output. This includes any signs, digits, grouping1093* separators, radix indicator, and parentheses. If the length of the1094* converted value is less than the width then the output will be padded by1095* spaces (<code>'\u0020'</code>) until the total number of characters equals1096* width. The padding is on the left by default. If {@code '-'} flag is1097* given then the padding will be on the right. If width is not specified then1098* there is no minimum.1099*1100* <p> The precision is not applicable. If precision is specified then an1101* {@link IllegalFormatPrecisionException} will be thrown.1102*1103* <p><a id="dnbint"><b> BigInteger </b></a>1104*1105* <p> The following conversions may be applied to {@link1106* java.math.BigInteger}.1107*1108* <table class="striped">1109* <caption style="display:none">bIntConv</caption>1110* <tbody>1111*1112* <tr><th scope="row" style="vertical-align:top"> {@code 'd'}1113* <td style="vertical-align:top"> <code>'\u0064'</code>1114* <td> Requires the output to be formatted as a decimal integer. The <a1115* href="#L10nAlgorithm">localization algorithm</a> is applied.1116*1117* <p> If the {@code '#'} flag is given {@link1118* FormatFlagsConversionMismatchException} will be thrown.1119*1120* <tr><th scope="row" style="vertical-align:top"> {@code 'o'}1121* <td style="vertical-align:top"> <code>'\u006f'</code>1122* <td> Requires the output to be formatted as an integer in base eight.1123* No localization is applied.1124*1125* <p> If <i>x</i> is negative then the result will be a signed value1126* beginning with {@code '-'} (<code>'\u002d'</code>). Signed output is1127* allowed for this type because unlike the primitive types it is not1128* possible to create an unsigned equivalent without assuming an explicit1129* data-type size.1130*1131* <p> If <i>x</i> is positive or zero and the {@code '+'} flag is given1132* then the result will begin with {@code '+'} (<code>'\u002b'</code>).1133*1134* <p> If the {@code '#'} flag is given then the output will always begin1135* with {@code '0'} prefix.1136*1137* <p> If the {@code '0'} flag is given then the output will be padded1138* with leading zeros to the field width following any indication of sign.1139*1140* <p> If the {@code ','} flag is given then a {@link1141* FormatFlagsConversionMismatchException} will be thrown.1142*1143* <tr><th scope="row" style="vertical-align:top"> {@code 'x'}1144* <td style="vertical-align:top"> <code>'\u0078'</code>1145* <td> Requires the output to be formatted as an integer in base1146* sixteen. No localization is applied.1147*1148* <p> If <i>x</i> is negative then the result will be a signed value1149* beginning with {@code '-'} (<code>'\u002d'</code>). Signed output is1150* allowed for this type because unlike the primitive types it is not1151* possible to create an unsigned equivalent without assuming an explicit1152* data-type size.1153*1154* <p> If <i>x</i> is positive or zero and the {@code '+'} flag is given1155* then the result will begin with {@code '+'} (<code>'\u002b'</code>).1156*1157* <p> If the {@code '#'} flag is given then the output will always begin1158* with the radix indicator {@code "0x"}.1159*1160* <p> If the {@code '0'} flag is given then the output will be padded to1161* the field width with leading zeros after the radix indicator or sign (if1162* present).1163*1164* <p> If the {@code ','} flag is given then a {@link1165* FormatFlagsConversionMismatchException} will be thrown.1166*1167* <tr><th scope="row" style="vertical-align:top"> {@code 'X'}1168* <td style="vertical-align:top"> <code>'\u0058'</code>1169* <td> The upper-case variant of {@code 'x'}. The entire string1170* representing the number will be converted to {@linkplain1171* String#toUpperCase upper case} including the {@code 'x'} (if any) and1172* all hexadecimal digits {@code 'a'} - {@code 'f'}1173* (<code>'\u0061'</code> - <code>'\u0066'</code>).1174*1175* </tbody>1176* </table>1177*1178* <p> If the conversion is {@code 'o'}, {@code 'x'}, or {@code 'X'} and1179* both the {@code '#'} and the {@code '0'} flags are given, then result will1180* contain the base indicator ({@code '0'} for octal and {@code "0x"} or1181* {@code "0X"} for hexadecimal), some number of zeros (based on the width),1182* and the value.1183*1184* <p> If the {@code '0'} flag is given and the value is negative, then the1185* zero padding will occur after the sign.1186*1187* <p> If the {@code '-'} flag is not given, then the space padding will occur1188* before the sign.1189*1190* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and1191* Long apply. The <a href="#intdFlags">default behavior</a> when no flags are1192* given is the same as for Byte, Short, Integer, and Long.1193*1194* <p> The specification of <a href="#intWidth">width</a> is the same as1195* defined for Byte, Short, Integer, and Long.1196*1197* <p> The precision is not applicable. If precision is specified then an1198* {@link IllegalFormatPrecisionException} will be thrown.1199*1200* <p><a id="dndec"><b> Float and Double</b></a>1201*1202* <p> The following conversions may be applied to {@code float}, {@link1203* Float}, {@code double} and {@link Double}.1204*1205* <table class="striped">1206* <caption style="display:none">floatConv</caption>1207* <tbody>1208*1209* <tr><th scope="row" style="vertical-align:top"> {@code 'e'}1210* <td style="vertical-align:top"> <code>'\u0065'</code>1211* <td> Requires the output to be formatted using <a1212* id="scientific">computerized scientific notation</a>. The <a1213* href="#L10nAlgorithm">localization algorithm</a> is applied.1214*1215* <p> The formatting of the magnitude <i>m</i> depends upon its value.1216*1217* <p> If <i>m</i> is NaN or infinite, the literal strings "NaN" or1218* "Infinity", respectively, will be output. These values are not1219* localized.1220*1221* <p> If <i>m</i> is positive-zero or negative-zero, then the exponent1222* will be {@code "+00"}.1223*1224* <p> Otherwise, the result is a string that represents the sign and1225* magnitude (absolute value) of the argument. The formatting of the sign1226* is described in the <a href="#L10nAlgorithm">localization1227* algorithm</a>. The formatting of the magnitude <i>m</i> depends upon its1228* value.1229*1230* <p> Let <i>n</i> be the unique integer such that 10<sup><i>n</i></sup>1231* <= <i>m</i> < 10<sup><i>n</i>+1</sup>; then let <i>a</i> be the1232* mathematically exact quotient of <i>m</i> and 10<sup><i>n</i></sup> so1233* that 1 <= <i>a</i> < 10. The magnitude is then represented as the1234* integer part of <i>a</i>, as a single decimal digit, followed by the1235* decimal separator followed by decimal digits representing the fractional1236* part of <i>a</i>, followed by the exponent symbol {@code 'e'}1237* (<code>'\u0065'</code>), followed by the sign of the exponent, followed1238* by a representation of <i>n</i> as a decimal integer, as produced by the1239* method {@link Long#toString(long, int)}, and zero-padded to include at1240* least two digits.1241*1242* <p> The number of digits in the result for the fractional part of1243* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not1244* specified then the default value is {@code 6}. If the precision is less1245* than the number of digits which would appear after the decimal point in1246* the string returned by {@link Float#toString(float)} or {@link1247* Double#toString(double)} respectively, then the value will be rounded1248* using the {@linkplain java.math.RoundingMode#HALF_UP round half up1249* algorithm}. Otherwise, zeros may be appended to reach the precision.1250* For a canonical representation of the value, use {@link1251* Float#toString(float)} or {@link Double#toString(double)} as1252* appropriate.1253*1254* <p>If the {@code ','} flag is given, then an {@link1255* FormatFlagsConversionMismatchException} will be thrown.1256*1257* <tr><th scope="row" style="vertical-align:top"> {@code 'E'}1258* <td style="vertical-align:top"> <code>'\u0045'</code>1259* <td> The upper-case variant of {@code 'e'}. The exponent symbol1260* will be {@code 'E'} (<code>'\u0045'</code>).1261*1262* <tr><th scope="row" style="vertical-align:top"> {@code 'g'}1263* <td style="vertical-align:top"> <code>'\u0067'</code>1264* <td> Requires the output to be formatted in general scientific notation1265* as described below. The <a href="#L10nAlgorithm">localization1266* algorithm</a> is applied.1267*1268* <p> After rounding for the precision, the formatting of the resulting1269* magnitude <i>m</i> depends on its value.1270*1271* <p> If <i>m</i> is greater than or equal to 10<sup>-4</sup> but less1272* than 10<sup>precision</sup> then it is represented in <i><a1273* href="#decimal">decimal format</a></i>.1274*1275* <p> If <i>m</i> is less than 10<sup>-4</sup> or greater than or equal to1276* 10<sup>precision</sup>, then it is represented in <i><a1277* href="#scientific">computerized scientific notation</a></i>.1278*1279* <p> The total number of significant digits in <i>m</i> is equal to the1280* precision. If the precision is not specified, then the default value is1281* {@code 6}. If the precision is {@code 0}, then it is taken to be1282* {@code 1}.1283*1284* <p> If the {@code '#'} flag is given then an {@link1285* FormatFlagsConversionMismatchException} will be thrown.1286*1287* <tr><th scope="row" style="vertical-align:top"> {@code 'G'}1288* <td style="vertical-align:top"> <code>'\u0047'</code>1289* <td> The upper-case variant of {@code 'g'}.1290*1291* <tr><th scope="row" style="vertical-align:top"> {@code 'f'}1292* <td style="vertical-align:top"> <code>'\u0066'</code>1293* <td> Requires the output to be formatted using <a id="decimal">decimal1294* format</a>. The <a href="#L10nAlgorithm">localization algorithm</a> is1295* applied.1296*1297* <p> The result is a string that represents the sign and magnitude1298* (absolute value) of the argument. The formatting of the sign is1299* described in the <a href="#L10nAlgorithm">localization1300* algorithm</a>. The formatting of the magnitude <i>m</i> depends upon its1301* value.1302*1303* <p> If <i>m</i> NaN or infinite, the literal strings "NaN" or1304* "Infinity", respectively, will be output. These values are not1305* localized.1306*1307* <p> The magnitude is formatted as the integer part of <i>m</i>, with no1308* leading zeroes, followed by the decimal separator followed by one or1309* more decimal digits representing the fractional part of <i>m</i>.1310*1311* <p> The number of digits in the result for the fractional part of1312* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not1313* specified then the default value is {@code 6}. If the precision is less1314* than the number of digits which would appear after the decimal point in1315* the string returned by {@link Float#toString(float)} or {@link1316* Double#toString(double)} respectively, then the value will be rounded1317* using the {@linkplain java.math.RoundingMode#HALF_UP round half up1318* algorithm}. Otherwise, zeros may be appended to reach the precision.1319* For a canonical representation of the value, use {@link1320* Float#toString(float)} or {@link Double#toString(double)} as1321* appropriate.1322*1323* <tr><th scope="row" style="vertical-align:top"> {@code 'a'}1324* <td style="vertical-align:top"> <code>'\u0061'</code>1325* <td> Requires the output to be formatted in hexadecimal exponential1326* form. No localization is applied.1327*1328* <p> The result is a string that represents the sign and magnitude1329* (absolute value) of the argument <i>x</i>.1330*1331* <p> If <i>x</i> is negative or a negative-zero value then the result1332* will begin with {@code '-'} (<code>'\u002d'</code>).1333*1334* <p> If <i>x</i> is positive or a positive-zero value and the1335* {@code '+'} flag is given then the result will begin with {@code '+'}1336* (<code>'\u002b'</code>).1337*1338* <p> The formatting of the magnitude <i>m</i> depends upon its value.1339*1340* <ul>1341*1342* <li> If the value is NaN or infinite, the literal strings "NaN" or1343* "Infinity", respectively, will be output.1344*1345* <li> If <i>m</i> is zero then it is represented by the string1346* {@code "0x0.0p0"}.1347*1348* <li> If <i>m</i> is a {@code double} value with a normalized1349* representation then substrings are used to represent the significand and1350* exponent fields. The significand is represented by the characters1351* {@code "0x1."} followed by the hexadecimal representation of the rest1352* of the significand as a fraction. The exponent is represented by1353* {@code 'p'} (<code>'\u0070'</code>) followed by a decimal string of the1354* unbiased exponent as if produced by invoking {@link1355* Integer#toString(int) Integer.toString} on the exponent value. If the1356* precision is specified, the value is rounded to the given number of1357* hexadecimal digits.1358*1359* <li> If <i>m</i> is a {@code double} value with a subnormal1360* representation then, unless the precision is specified to be in the range1361* 1 through 12, inclusive, the significand is represented by the characters1362* {@code '0x0.'} followed by the hexadecimal representation of the rest of1363* the significand as a fraction, and the exponent represented by1364* {@code 'p-1022'}. If the precision is in the interval1365* [1, 12], the subnormal value is normalized such that it1366* begins with the characters {@code '0x1.'}, rounded to the number of1367* hexadecimal digits of precision, and the exponent adjusted1368* accordingly. Note that there must be at least one nonzero digit in a1369* subnormal significand.1370*1371* </ul>1372*1373* <p> If the {@code '('} or {@code ','} flags are given, then a {@link1374* FormatFlagsConversionMismatchException} will be thrown.1375*1376* <tr><th scope="row" style="vertical-align:top"> {@code 'A'}1377* <td style="vertical-align:top"> <code>'\u0041'</code>1378* <td> The upper-case variant of {@code 'a'}. The entire string1379* representing the number will be converted to upper case including the1380* {@code 'x'} (<code>'\u0078'</code>) and {@code 'p'}1381* (<code>'\u0070'</code> and all hexadecimal digits {@code 'a'} -1382* {@code 'f'} (<code>'\u0061'</code> - <code>'\u0066'</code>).1383*1384* </tbody>1385* </table>1386*1387* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and1388* Long apply.1389*1390* <p> If the {@code '#'} flag is given, then the decimal separator will1391* always be present.1392*1393* <p> If no <a id="floatdFlags">flags</a> are given the default formatting1394* is as follows:1395*1396* <ul>1397*1398* <li> The output is right-justified within the {@code width}1399*1400* <li> Negative numbers begin with a {@code '-'}1401*1402* <li> Positive numbers and positive zero do not include a sign or extra1403* leading space1404*1405* <li> No grouping separators are included1406*1407* <li> The decimal separator will only appear if a digit follows it1408*1409* </ul>1410*1411* <p> The <a id="floatDWidth">width</a> is the minimum number of characters1412* to be written to the output. This includes any signs, digits, grouping1413* separators, decimal separators, exponential symbol, radix indicator,1414* parentheses, and strings representing infinity and NaN as applicable. If1415* the length of the converted value is less than the width then the output1416* will be padded by spaces (<code>'\u0020'</code>) until the total number of1417* characters equals width. The padding is on the left by default. If the1418* {@code '-'} flag is given then the padding will be on the right. If width1419* is not specified then there is no minimum.1420*1421* <p> If the <a id="floatDPrec">conversion</a> is {@code 'e'},1422* {@code 'E'} or {@code 'f'}, then the precision is the number of digits1423* after the decimal separator. If the precision is not specified, then it is1424* assumed to be {@code 6}.1425*1426* <p> If the conversion is {@code 'g'} or {@code 'G'}, then the precision is1427* the total number of significant digits in the resulting magnitude after1428* rounding. If the precision is not specified, then the default value is1429* {@code 6}. If the precision is {@code 0}, then it is taken to be1430* {@code 1}.1431*1432* <p> If the conversion is {@code 'a'} or {@code 'A'}, then the precision1433* is the number of hexadecimal digits after the radix point. If the1434* precision is not provided, then all of the digits as returned by {@link1435* Double#toHexString(double)} will be output.1436*1437* <p><a id="dnbdec"><b> BigDecimal </b></a>1438*1439* <p> The following conversions may be applied {@link java.math.BigDecimal1440* BigDecimal}.1441*1442* <table class="striped">1443* <caption style="display:none">floatConv</caption>1444* <tbody>1445*1446* <tr><th scope="row" style="vertical-align:top"> {@code 'e'}1447* <td style="vertical-align:top"> <code>'\u0065'</code>1448* <td> Requires the output to be formatted using <a1449* id="bscientific">computerized scientific notation</a>. The <a1450* href="#L10nAlgorithm">localization algorithm</a> is applied.1451*1452* <p> The formatting of the magnitude <i>m</i> depends upon its value.1453*1454* <p> If <i>m</i> is positive-zero or negative-zero, then the exponent1455* will be {@code "+00"}.1456*1457* <p> Otherwise, the result is a string that represents the sign and1458* magnitude (absolute value) of the argument. The formatting of the sign1459* is described in the <a href="#L10nAlgorithm">localization1460* algorithm</a>. The formatting of the magnitude <i>m</i> depends upon its1461* value.1462*1463* <p> Let <i>n</i> be the unique integer such that 10<sup><i>n</i></sup>1464* <= <i>m</i> < 10<sup><i>n</i>+1</sup>; then let <i>a</i> be the1465* mathematically exact quotient of <i>m</i> and 10<sup><i>n</i></sup> so1466* that 1 <= <i>a</i> < 10. The magnitude is then represented as the1467* integer part of <i>a</i>, as a single decimal digit, followed by the1468* decimal separator followed by decimal digits representing the fractional1469* part of <i>a</i>, followed by the exponent symbol {@code 'e'}1470* (<code>'\u0065'</code>), followed by the sign of the exponent, followed1471* by a representation of <i>n</i> as a decimal integer, as produced by the1472* method {@link Long#toString(long, int)}, and zero-padded to include at1473* least two digits.1474*1475* <p> The number of digits in the result for the fractional part of1476* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not1477* specified then the default value is {@code 6}. If the precision is1478* less than the number of digits to the right of the decimal point then1479* the value will be rounded using the1480* {@linkplain java.math.RoundingMode#HALF_UP round half up1481* algorithm}. Otherwise, zeros may be appended to reach the precision.1482* For a canonical representation of the value, use {@link1483* BigDecimal#toString()}.1484*1485* <p> If the {@code ','} flag is given, then an {@link1486* FormatFlagsConversionMismatchException} will be thrown.1487*1488* <tr><th scope="row" style="vertical-align:top"> {@code 'E'}1489* <td style="vertical-align:top"> <code>'\u0045'</code>1490* <td> The upper-case variant of {@code 'e'}. The exponent symbol1491* will be {@code 'E'} (<code>'\u0045'</code>).1492*1493* <tr><th scope="row" style="vertical-align:top"> {@code 'g'}1494* <td style="vertical-align:top"> <code>'\u0067'</code>1495* <td> Requires the output to be formatted in general scientific notation1496* as described below. The <a href="#L10nAlgorithm">localization1497* algorithm</a> is applied.1498*1499* <p> After rounding for the precision, the formatting of the resulting1500* magnitude <i>m</i> depends on its value.1501*1502* <p> If <i>m</i> is greater than or equal to 10<sup>-4</sup> but less1503* than 10<sup>precision</sup> then it is represented in <i><a1504* href="#bdecimal">decimal format</a></i>.1505*1506* <p> If <i>m</i> is less than 10<sup>-4</sup> or greater than or equal to1507* 10<sup>precision</sup>, then it is represented in <i><a1508* href="#bscientific">computerized scientific notation</a></i>.1509*1510* <p> The total number of significant digits in <i>m</i> is equal to the1511* precision. If the precision is not specified, then the default value is1512* {@code 6}. If the precision is {@code 0}, then it is taken to be1513* {@code 1}.1514*1515* <p> If the {@code '#'} flag is given then an {@link1516* FormatFlagsConversionMismatchException} will be thrown.1517*1518* <tr><th scope="row" style="vertical-align:top"> {@code 'G'}1519* <td style="vertical-align:top"> <code>'\u0047'</code>1520* <td> The upper-case variant of {@code 'g'}.1521*1522* <tr><th scope="row" style="vertical-align:top"> {@code 'f'}1523* <td style="vertical-align:top"> <code>'\u0066'</code>1524* <td> Requires the output to be formatted using <a id="bdecimal">decimal1525* format</a>. The <a href="#L10nAlgorithm">localization algorithm</a> is1526* applied.1527*1528* <p> The result is a string that represents the sign and magnitude1529* (absolute value) of the argument. The formatting of the sign is1530* described in the <a href="#L10nAlgorithm">localization1531* algorithm</a>. The formatting of the magnitude <i>m</i> depends upon its1532* value.1533*1534* <p> The magnitude is formatted as the integer part of <i>m</i>, with no1535* leading zeroes, followed by the decimal separator followed by one or1536* more decimal digits representing the fractional part of <i>m</i>.1537*1538* <p> The number of digits in the result for the fractional part of1539* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not1540* specified then the default value is {@code 6}. If the precision is1541* less than the number of digits to the right of the decimal point1542* then the value will be rounded using the1543* {@linkplain java.math.RoundingMode#HALF_UP round half up1544* algorithm}. Otherwise, zeros may be appended to reach the precision.1545* For a canonical representation of the value, use {@link1546* BigDecimal#toString()}.1547*1548* </tbody>1549* </table>1550*1551* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and1552* Long apply.1553*1554* <p> If the {@code '#'} flag is given, then the decimal separator will1555* always be present.1556*1557* <p> The <a href="#floatdFlags">default behavior</a> when no flags are1558* given is the same as for Float and Double.1559*1560* <p> The specification of <a href="#floatDWidth">width</a> and <a1561* href="#floatDPrec">precision</a> is the same as defined for Float and1562* Double.1563*1564* <h3><a id="ddt">Date/Time</a></h3>1565*1566* <p> This conversion may be applied to {@code long}, {@link Long}, {@link1567* Calendar}, {@link Date} and {@link TemporalAccessor TemporalAccessor}1568*1569* <table class="striped">1570* <caption style="display:none">DTConv</caption>1571* <tbody>1572*1573* <tr><th scope="row" style="vertical-align:top"> {@code 't'}1574* <td style="vertical-align:top"> <code>'\u0074'</code>1575* <td> Prefix for date and time conversion characters.1576* <tr><th scope="row" style="vertical-align:top"> {@code 'T'}1577* <td style="vertical-align:top"> <code>'\u0054'</code>1578* <td> The upper-case variant of {@code 't'}.1579*1580* </tbody>1581* </table>1582*1583* <p> The following date and time conversion character suffixes are defined1584* for the {@code 't'} and {@code 'T'} conversions. The types are similar to1585* but not completely identical to those defined by GNU {@code date} and1586* POSIX {@code strftime(3c)}. Additional conversion types are provided to1587* access Java-specific functionality (e.g. {@code 'L'} for milliseconds1588* within the second).1589*1590* <p> The following conversion characters are used for formatting times:1591*1592* <table class="striped">1593* <caption style="display:none">time</caption>1594* <tbody>1595*1596* <tr><th scope="row" style="vertical-align:top"> {@code 'H'}1597* <td style="vertical-align:top"> <code>'\u0048'</code>1598* <td> Hour of the day for the 24-hour clock, formatted as two digits with1599* a leading zero as necessary i.e. {@code 00 - 23}. {@code 00}1600* corresponds to midnight.1601*1602* <tr><th scope="row" style="vertical-align:top">{@code 'I'}1603* <td style="vertical-align:top"> <code>'\u0049'</code>1604* <td> Hour for the 12-hour clock, formatted as two digits with a leading1605* zero as necessary, i.e. {@code 01 - 12}. {@code 01} corresponds to1606* one o'clock (either morning or afternoon).1607*1608* <tr><th scope="row" style="vertical-align:top">{@code 'k'}1609* <td style="vertical-align:top"> <code>'\u006b'</code>1610* <td> Hour of the day for the 24-hour clock, i.e. {@code 0 - 23}.1611* {@code 0} corresponds to midnight.1612*1613* <tr><th scope="row" style="vertical-align:top">{@code 'l'}1614* <td style="vertical-align:top"> <code>'\u006c'</code>1615* <td> Hour for the 12-hour clock, i.e. {@code 1 - 12}. {@code 1}1616* corresponds to one o'clock (either morning or afternoon).1617*1618* <tr><th scope="row" style="vertical-align:top">{@code 'M'}1619* <td style="vertical-align:top"> <code>'\u004d'</code>1620* <td> Minute within the hour formatted as two digits with a leading zero1621* as necessary, i.e. {@code 00 - 59}.1622*1623* <tr><th scope="row" style="vertical-align:top">{@code 'S'}1624* <td style="vertical-align:top"> <code>'\u0053'</code>1625* <td> Seconds within the minute, formatted as two digits with a leading1626* zero as necessary, i.e. {@code 00 - 60} ("{@code 60}" is a special1627* value required to support leap seconds).1628*1629* <tr><th scope="row" style="vertical-align:top">{@code 'L'}1630* <td style="vertical-align:top"> <code>'\u004c'</code>1631* <td> Millisecond within the second formatted as three digits with1632* leading zeros as necessary, i.e. {@code 000 - 999}.1633*1634* <tr><th scope="row" style="vertical-align:top">{@code 'N'}1635* <td style="vertical-align:top"> <code>'\u004e'</code>1636* <td> Nanosecond within the second, formatted as nine digits with leading1637* zeros as necessary, i.e. {@code 000000000 - 999999999}. The precision1638* of this value is limited by the resolution of the underlying operating1639* system or hardware.1640*1641* <tr><th scope="row" style="vertical-align:top">{@code 'p'}1642* <td style="vertical-align:top"> <code>'\u0070'</code>1643* <td> Locale-specific {@linkplain1644* java.text.DateFormatSymbols#getAmPmStrings morning or afternoon} marker1645* in lower case, e.g."{@code am}" or "{@code pm}". Use of the1646* conversion prefix {@code 'T'} forces this output to upper case. (Note1647* that {@code 'p'} produces lower-case output. This is different from1648* GNU {@code date} and POSIX {@code strftime(3c)} which produce1649* upper-case output.)1650*1651* <tr><th scope="row" style="vertical-align:top">{@code 'z'}1652* <td style="vertical-align:top"> <code>'\u007a'</code>1653* <td> <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC 822</a>1654* style numeric time zone offset from GMT, e.g. {@code -0800}. This1655* value will be adjusted as necessary for Daylight Saving Time. For1656* {@code long}, {@link Long}, and {@link Date} the time zone used is1657* the {@linkplain TimeZone#getDefault() default time zone} for this1658* instance of the Java virtual machine.1659*1660* <tr><th scope="row" style="vertical-align:top">{@code 'Z'}1661* <td style="vertical-align:top"> <code>'\u005a'</code>1662* <td> A string representing the abbreviation for the time zone. This1663* value will be adjusted as necessary for Daylight Saving Time. For1664* {@code long}, {@link Long}, and {@link Date} the time zone used is1665* the {@linkplain TimeZone#getDefault() default time zone} for this1666* instance of the Java virtual machine. The Formatter's locale will1667* supersede the locale of the argument (if any).1668*1669* <tr><th scope="row" style="vertical-align:top">{@code 's'}1670* <td style="vertical-align:top"> <code>'\u0073'</code>1671* <td> Seconds since the beginning of the epoch starting at 1 January 19701672* {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE/1000} to1673* {@code Long.MAX_VALUE/1000}.1674*1675* <tr><th scope="row" style="vertical-align:top">{@code 'Q'}1676* <td style="vertical-align:top"> <code>'\u004f'</code>1677* <td> Milliseconds since the beginning of the epoch starting at 1 January1678* 1970 {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE} to1679* {@code Long.MAX_VALUE}. The precision of this value is limited by1680* the resolution of the underlying operating system or hardware.1681*1682* </tbody>1683* </table>1684*1685* <p> The following conversion characters are used for formatting dates:1686*1687* <table class="striped">1688* <caption style="display:none">date</caption>1689* <tbody>1690*1691* <tr><th scope="row" style="vertical-align:top">{@code 'B'}1692* <td style="vertical-align:top"> <code>'\u0042'</code>1693* <td> Locale-specific {@linkplain java.text.DateFormatSymbols#getMonths1694* full month name}, e.g. {@code "January"}, {@code "February"}.1695*1696* <tr><th scope="row" style="vertical-align:top">{@code 'b'}1697* <td style="vertical-align:top"> <code>'\u0062'</code>1698* <td> Locale-specific {@linkplain1699* java.text.DateFormatSymbols#getShortMonths abbreviated month name},1700* e.g. {@code "Jan"}, {@code "Feb"}.1701*1702* <tr><th scope="row" style="vertical-align:top">{@code 'h'}1703* <td style="vertical-align:top"> <code>'\u0068'</code>1704* <td> Same as {@code 'b'}.1705*1706* <tr><th scope="row" style="vertical-align:top">{@code 'A'}1707* <td style="vertical-align:top"> <code>'\u0041'</code>1708* <td> Locale-specific full name of the {@linkplain1709* java.text.DateFormatSymbols#getWeekdays day of the week},1710* e.g. {@code "Sunday"}, {@code "Monday"}1711*1712* <tr><th scope="row" style="vertical-align:top">{@code 'a'}1713* <td style="vertical-align:top"> <code>'\u0061'</code>1714* <td> Locale-specific short name of the {@linkplain1715* java.text.DateFormatSymbols#getShortWeekdays day of the week},1716* e.g. {@code "Sun"}, {@code "Mon"}1717*1718* <tr><th scope="row" style="vertical-align:top">{@code 'C'}1719* <td style="vertical-align:top"> <code>'\u0043'</code>1720* <td> Four-digit year divided by {@code 100}, formatted as two digits1721* with leading zero as necessary, i.e. {@code 00 - 99}1722*1723* <tr><th scope="row" style="vertical-align:top">{@code 'Y'}1724* <td style="vertical-align:top"> <code>'\u0059'</code> <td> Year, formatted to at least1725* four digits with leading zeros as necessary, e.g. {@code 0092} equals1726* {@code 92} CE for the Gregorian calendar.1727*1728* <tr><th scope="row" style="vertical-align:top">{@code 'y'}1729* <td style="vertical-align:top"> <code>'\u0079'</code>1730* <td> Last two digits of the year, formatted with leading zeros as1731* necessary, i.e. {@code 00 - 99}.1732*1733* <tr><th scope="row" style="vertical-align:top">{@code 'j'}1734* <td style="vertical-align:top"> <code>'\u006a'</code>1735* <td> Day of year, formatted as three digits with leading zeros as1736* necessary, e.g. {@code 001 - 366} for the Gregorian calendar.1737* {@code 001} corresponds to the first day of the year.1738*1739* <tr><th scope="row" style="vertical-align:top">{@code 'm'}1740* <td style="vertical-align:top"> <code>'\u006d'</code>1741* <td> Month, formatted as two digits with leading zeros as necessary,1742* i.e. {@code 01 - 13}, where "{@code 01}" is the first month of the1743* year and ("{@code 13}" is a special value required to support lunar1744* calendars).1745*1746* <tr><th scope="row" style="vertical-align:top">{@code 'd'}1747* <td style="vertical-align:top"> <code>'\u0064'</code>1748* <td> Day of month, formatted as two digits with leading zeros as1749* necessary, i.e. {@code 01 - 31}, where "{@code 01}" is the first day1750* of the month.1751*1752* <tr><th scope="row" style="vertical-align:top">{@code 'e'}1753* <td style="vertical-align:top"> <code>'\u0065'</code>1754* <td> Day of month, formatted as two digits, i.e. {@code 1 - 31} where1755* "{@code 1}" is the first day of the month.1756*1757* </tbody>1758* </table>1759*1760* <p> The following conversion characters are used for formatting common1761* date/time compositions.1762*1763* <table class="striped">1764* <caption style="display:none">composites</caption>1765* <tbody>1766*1767* <tr><th scope="row" style="vertical-align:top">{@code 'R'}1768* <td style="vertical-align:top"> <code>'\u0052'</code>1769* <td> Time formatted for the 24-hour clock as {@code "%tH:%tM"}1770*1771* <tr><th scope="row" style="vertical-align:top">{@code 'T'}1772* <td style="vertical-align:top"> <code>'\u0054'</code>1773* <td> Time formatted for the 24-hour clock as {@code "%tH:%tM:%tS"}.1774*1775* <tr><th scope="row" style="vertical-align:top">{@code 'r'}1776* <td style="vertical-align:top"> <code>'\u0072'</code>1777* <td> Time formatted for the 12-hour clock as {@code "%tI:%tM:%tS1778* %Tp"}. The location of the morning or afternoon marker1779* ({@code '%Tp'}) may be locale-dependent.1780*1781* <tr><th scope="row" style="vertical-align:top">{@code 'D'}1782* <td style="vertical-align:top"> <code>'\u0044'</code>1783* <td> Date formatted as {@code "%tm/%td/%ty"}.1784*1785* <tr><th scope="row" style="vertical-align:top">{@code 'F'}1786* <td style="vertical-align:top"> <code>'\u0046'</code>1787* <td> <a href="http://www.w3.org/TR/NOTE-datetime">ISO 8601</a>1788* complete date formatted as {@code "%tY-%tm-%td"}.1789*1790* <tr><th scope="row" style="vertical-align:top">{@code 'c'}1791* <td style="vertical-align:top"> <code>'\u0063'</code>1792* <td> Date and time formatted as {@code "%ta %tb %td %tT %tZ %tY"},1793* e.g. {@code "Sun Jul 20 16:17:00 EDT 1969"}.1794*1795* </tbody>1796* </table>1797*1798* <p> The {@code '-'} flag defined for <a href="#dFlags">General1799* conversions</a> applies. If the {@code '#'} flag is given, then a {@link1800* FormatFlagsConversionMismatchException} will be thrown.1801*1802* <p> The width is the minimum number of characters to1803* be written to the output. If the length of the converted value is less than1804* the {@code width} then the output will be padded by spaces1805* (<code>'\u0020'</code>) until the total number of characters equals width.1806* The padding is on the left by default. If the {@code '-'} flag is given1807* then the padding will be on the right. If width is not specified then there1808* is no minimum.1809*1810* <p> The precision is not applicable. If the precision is specified then an1811* {@link IllegalFormatPrecisionException} will be thrown.1812*1813* <h3><a id="dper">Percent</a></h3>1814*1815* <p> The conversion does not correspond to any argument.1816*1817* <table class="striped">1818* <caption style="display:none">DTConv</caption>1819* <tbody>1820*1821* <tr><th scope="row" style="vertical-align:top">{@code '%'}1822* <td> The result is a literal {@code '%'} (<code>'\u0025'</code>)1823*1824* <p> The width is the minimum number of characters to1825* be written to the output including the {@code '%'}. If the length of the1826* converted value is less than the {@code width} then the output will be1827* padded by spaces (<code>'\u0020'</code>) until the total number of1828* characters equals width. The padding is on the left. If width is not1829* specified then just the {@code '%'} is output.1830*1831* <p> The {@code '-'} flag defined for <a href="#dFlags">General1832* conversions</a> applies. If any other flags are provided, then a1833* {@link IllegalFormatFlagsException } will be thrown.1834*1835* <p> The precision is not applicable. If the precision is specified an1836* {@link IllegalFormatPrecisionException} will be thrown.1837*1838* </tbody>1839* </table>1840*1841* <h3><a id="dls">Line Separator</a></h3>1842*1843* <p> The conversion does not correspond to any argument.1844*1845* <table class="striped">1846* <caption style="display:none">DTConv</caption>1847* <tbody>1848*1849* <tr><th scope="row" style="vertical-align:top">{@code 'n'}1850* <td> the platform-specific line separator as returned by {@link1851* System#lineSeparator()}.1852*1853* </tbody>1854* </table>1855*1856* <p> Flags, width, and precision are not applicable. If any are provided an1857* {@link IllegalFormatFlagsException}, {@link IllegalFormatWidthException},1858* and {@link IllegalFormatPrecisionException}, respectively will be thrown.1859*1860* <h3><a id="dpos">Argument Index</a></h3>1861*1862* <p> Format specifiers can reference arguments in three ways:1863*1864* <ul>1865*1866* <li> <i>Explicit indexing</i> is used when the format specifier contains an1867* argument index. The argument index is a decimal integer indicating the1868* position of the argument in the argument list. The first argument is1869* referenced by "{@code 1$}", the second by "{@code 2$}", etc. An argument1870* may be referenced more than once.1871*1872* <p> For example:1873*1874* <blockquote><pre>1875* formatter.format("%4$s %3$s %2$s %1$s %4$s %3$s %2$s %1$s",1876* "a", "b", "c", "d")1877* // -> "d c b a d c b a"1878* </pre></blockquote>1879*1880* <li> <i>Relative indexing</i> is used when the format specifier contains a1881* {@code '<'} (<code>'\u003c'</code>) flag which causes the argument for1882* the previous format specifier to be re-used. If there is no previous1883* argument, then a {@link MissingFormatArgumentException} is thrown.1884*1885* <blockquote><pre>1886* formatter.format("%s %s %<s %<s", "a", "b", "c", "d")1887* // -> "a b b b"1888* // "c" and "d" are ignored because they are not referenced1889* </pre></blockquote>1890*1891* <li> <i>Ordinary indexing</i> is used when the format specifier contains1892* neither an argument index nor a {@code '<'} flag. Each format specifier1893* which uses ordinary indexing is assigned a sequential implicit index into1894* argument list which is independent of the indices used by explicit or1895* relative indexing.1896*1897* <blockquote><pre>1898* formatter.format("%s %s %s %s", "a", "b", "c", "d")1899* // -> "a b c d"1900* </pre></blockquote>1901*1902* </ul>1903*1904* <p> It is possible to have a format string which uses all forms of indexing,1905* for example:1906*1907* <blockquote><pre>1908* formatter.format("%2$s %s %<s %s", "a", "b", "c", "d")1909* // -> "b a a b"1910* // "c" and "d" are ignored because they are not referenced1911* </pre></blockquote>1912*1913* <p> The maximum number of arguments is limited by the maximum dimension of a1914* Java array as defined by1915* <cite>The Java Virtual Machine Specification</cite>.1916* If the argument index does not correspond to an1917* available argument, then a {@link MissingFormatArgumentException} is thrown.1918*1919* <p> If there are more arguments than format specifiers, the extra arguments1920* are ignored.1921*1922* <p> Unless otherwise specified, passing a {@code null} argument to any1923* method or constructor in this class will cause a {@link1924* NullPointerException} to be thrown.1925*1926* @author Iris Clark1927* @since 1.51928*/1929public final class Formatter implements Closeable, Flushable {1930private Appendable a;1931private final Locale l;19321933private IOException lastException;19341935// Non-character value used to mark zero as uninitialized1936private static final char ZERO_SENTINEL = '\uFFFE';1937private char zero = ZERO_SENTINEL;19381939/**1940* Returns a charset object for the given charset name.1941* @throws NullPointerException is csn is null1942* @throws UnsupportedEncodingException if the charset is not supported1943*/1944private static Charset toCharset(String csn)1945throws UnsupportedEncodingException1946{1947Objects.requireNonNull(csn, "charsetName");1948try {1949return Charset.forName(csn);1950} catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {1951// UnsupportedEncodingException should be thrown1952throw new UnsupportedEncodingException(csn);1953}1954}19551956private static final Appendable nonNullAppendable(Appendable a) {1957if (a == null)1958return new StringBuilder();19591960return a;1961}19621963/* Private constructors */1964private Formatter(Locale l, Appendable a) {1965this.a = a;1966this.l = l;1967}19681969private Formatter(Charset charset, Locale l, File file)1970throws FileNotFoundException1971{1972this(l,1973new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)));1974}19751976/**1977* Constructs a new formatter.1978*1979* <p> The destination of the formatted output is a {@link StringBuilder}1980* which may be retrieved by invoking {@link #out out()} and whose1981* current content may be converted into a string by invoking {@link1982* #toString toString()}. The locale used is the {@linkplain1983* Locale#getDefault(Locale.Category) default locale} for1984* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java1985* virtual machine.1986*/1987public Formatter() {1988this(Locale.getDefault(Locale.Category.FORMAT), new StringBuilder());1989}19901991/**1992* Constructs a new formatter with the specified destination.1993*1994* <p> The locale used is the {@linkplain1995* Locale#getDefault(Locale.Category) default locale} for1996* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java1997* virtual machine.1998*1999* @param a2000* Destination for the formatted output. If {@code a} is2001* {@code null} then a {@link StringBuilder} will be created.2002*/2003public Formatter(Appendable a) {2004this(Locale.getDefault(Locale.Category.FORMAT), nonNullAppendable(a));2005}20062007/**2008* Constructs a new formatter with the specified locale.2009*2010* <p> The destination of the formatted output is a {@link StringBuilder}2011* which may be retrieved by invoking {@link #out out()} and whose current2012* content may be converted into a string by invoking {@link #toString2013* toString()}.2014*2015* @param l2016* The {@linkplain java.util.Locale locale} to apply during2017* formatting. If {@code l} is {@code null} then no localization2018* is applied.2019*/2020public Formatter(Locale l) {2021this(l, new StringBuilder());2022}20232024/**2025* Constructs a new formatter with the specified destination and locale.2026*2027* @param a2028* Destination for the formatted output. If {@code a} is2029* {@code null} then a {@link StringBuilder} will be created.2030*2031* @param l2032* The {@linkplain java.util.Locale locale} to apply during2033* formatting. If {@code l} is {@code null} then no localization2034* is applied.2035*/2036public Formatter(Appendable a, Locale l) {2037this(l, nonNullAppendable(a));2038}20392040/**2041* Constructs a new formatter with the specified file name.2042*2043* <p> The charset used is the {@linkplain2044* java.nio.charset.Charset#defaultCharset() default charset} for this2045* instance of the Java virtual machine.2046*2047* <p> The locale used is the {@linkplain2048* Locale#getDefault(Locale.Category) default locale} for2049* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2050* virtual machine.2051*2052* @param fileName2053* The name of the file to use as the destination of this2054* formatter. If the file exists then it will be truncated to2055* zero size; otherwise, a new file will be created. The output2056* will be written to the file and is buffered.2057*2058* @throws SecurityException2059* If a security manager is present and {@link2060* SecurityManager#checkWrite checkWrite(fileName)} denies write2061* access to the file2062*2063* @throws FileNotFoundException2064* If the given file name does not denote an existing, writable2065* regular file and a new regular file of that name cannot be2066* created, or if some other error occurs while opening or2067* creating the file2068*/2069public Formatter(String fileName) throws FileNotFoundException {2070this(Locale.getDefault(Locale.Category.FORMAT),2071new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))));2072}20732074/**2075* Constructs a new formatter with the specified file name and charset.2076*2077* <p> The locale used is the {@linkplain2078* Locale#getDefault(Locale.Category) default locale} for2079* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2080* virtual machine.2081*2082* @param fileName2083* The name of the file to use as the destination of this2084* formatter. If the file exists then it will be truncated to2085* zero size; otherwise, a new file will be created. The output2086* will be written to the file and is buffered.2087*2088* @param csn2089* The name of a supported {@linkplain java.nio.charset.Charset2090* charset}2091*2092* @throws FileNotFoundException2093* If the given file name does not denote an existing, writable2094* regular file and a new regular file of that name cannot be2095* created, or if some other error occurs while opening or2096* creating the file2097*2098* @throws SecurityException2099* If a security manager is present and {@link2100* SecurityManager#checkWrite checkWrite(fileName)} denies write2101* access to the file2102*2103* @throws UnsupportedEncodingException2104* If the named charset is not supported2105*/2106public Formatter(String fileName, String csn)2107throws FileNotFoundException, UnsupportedEncodingException2108{2109this(fileName, csn, Locale.getDefault(Locale.Category.FORMAT));2110}21112112/**2113* Constructs a new formatter with the specified file name, charset, and2114* locale.2115*2116* @param fileName2117* The name of the file to use as the destination of this2118* formatter. If the file exists then it will be truncated to2119* zero size; otherwise, a new file will be created. The output2120* will be written to the file and is buffered.2121*2122* @param csn2123* The name of a supported {@linkplain java.nio.charset.Charset2124* charset}2125*2126* @param l2127* The {@linkplain java.util.Locale locale} to apply during2128* formatting. If {@code l} is {@code null} then no localization2129* is applied.2130*2131* @throws FileNotFoundException2132* If the given file name does not denote an existing, writable2133* regular file and a new regular file of that name cannot be2134* created, or if some other error occurs while opening or2135* creating the file2136*2137* @throws SecurityException2138* If a security manager is present and {@link2139* SecurityManager#checkWrite checkWrite(fileName)} denies write2140* access to the file2141*2142* @throws UnsupportedEncodingException2143* If the named charset is not supported2144*/2145public Formatter(String fileName, String csn, Locale l)2146throws FileNotFoundException, UnsupportedEncodingException2147{2148this(toCharset(csn), l, new File(fileName));2149}21502151/**2152* Constructs a new formatter with the specified file name, charset, and2153* locale.2154*2155* @param fileName2156* The name of the file to use as the destination of this2157* formatter. If the file exists then it will be truncated to2158* zero size; otherwise, a new file will be created. The output2159* will be written to the file and is buffered.2160*2161* @param charset2162* A {@linkplain java.nio.charset.Charset charset}2163*2164* @param l2165* The {@linkplain java.util.Locale locale} to apply during2166* formatting. If {@code l} is {@code null} then no localization2167* is applied.2168*2169* @throws IOException2170* if an I/O error occurs while opening or creating the file2171*2172* @throws SecurityException2173* If a security manager is present and {@link2174* SecurityManager#checkWrite checkWrite(fileName)} denies write2175* access to the file2176*2177* @throws NullPointerException2178* if {@code fileName} or {@code charset} is {@code null}.2179*/2180public Formatter(String fileName, Charset charset, Locale l) throws IOException {2181this(Objects.requireNonNull(charset, "charset"), l, new File(fileName));2182}21832184/**2185* Constructs a new formatter with the specified file.2186*2187* <p> The charset used is the {@linkplain2188* java.nio.charset.Charset#defaultCharset() default charset} for this2189* instance of the Java virtual machine.2190*2191* <p> The locale used is the {@linkplain2192* Locale#getDefault(Locale.Category) default locale} for2193* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2194* virtual machine.2195*2196* @param file2197* The file to use as the destination of this formatter. If the2198* file exists then it will be truncated to zero size; otherwise,2199* a new file will be created. The output will be written to the2200* file and is buffered.2201*2202* @throws SecurityException2203* If a security manager is present and {@link2204* SecurityManager#checkWrite checkWrite(file.getPath())} denies2205* write access to the file2206*2207* @throws FileNotFoundException2208* If the given file object does not denote an existing, writable2209* regular file and a new regular file of that name cannot be2210* created, or if some other error occurs while opening or2211* creating the file2212*/2213public Formatter(File file) throws FileNotFoundException {2214this(Locale.getDefault(Locale.Category.FORMAT),2215new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))));2216}22172218/**2219* Constructs a new formatter with the specified file and charset.2220*2221* <p> The locale used is the {@linkplain2222* Locale#getDefault(Locale.Category) default locale} for2223* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2224* virtual machine.2225*2226* @param file2227* The file to use as the destination of this formatter. If the2228* file exists then it will be truncated to zero size; otherwise,2229* a new file will be created. The output will be written to the2230* file and is buffered.2231*2232* @param csn2233* The name of a supported {@linkplain java.nio.charset.Charset2234* charset}2235*2236* @throws FileNotFoundException2237* If the given file object does not denote an existing, writable2238* regular file and a new regular file of that name cannot be2239* created, or if some other error occurs while opening or2240* creating the file2241*2242* @throws SecurityException2243* If a security manager is present and {@link2244* SecurityManager#checkWrite checkWrite(file.getPath())} denies2245* write access to the file2246*2247* @throws UnsupportedEncodingException2248* If the named charset is not supported2249*/2250public Formatter(File file, String csn)2251throws FileNotFoundException, UnsupportedEncodingException2252{2253this(file, csn, Locale.getDefault(Locale.Category.FORMAT));2254}22552256/**2257* Constructs a new formatter with the specified file, charset, and2258* locale.2259*2260* @param file2261* The file to use as the destination of this formatter. If the2262* file exists then it will be truncated to zero size; otherwise,2263* a new file will be created. The output will be written to the2264* file and is buffered.2265*2266* @param csn2267* The name of a supported {@linkplain java.nio.charset.Charset2268* charset}2269*2270* @param l2271* The {@linkplain java.util.Locale locale} to apply during2272* formatting. If {@code l} is {@code null} then no localization2273* is applied.2274*2275* @throws FileNotFoundException2276* If the given file object does not denote an existing, writable2277* regular file and a new regular file of that name cannot be2278* created, or if some other error occurs while opening or2279* creating the file2280*2281* @throws SecurityException2282* If a security manager is present and {@link2283* SecurityManager#checkWrite checkWrite(file.getPath())} denies2284* write access to the file2285*2286* @throws UnsupportedEncodingException2287* If the named charset is not supported2288*/2289public Formatter(File file, String csn, Locale l)2290throws FileNotFoundException, UnsupportedEncodingException2291{2292this(toCharset(csn), l, file);2293}22942295/**2296* Constructs a new formatter with the specified file, charset, and2297* locale.2298*2299* @param file2300* The file to use as the destination of this formatter. If the2301* file exists then it will be truncated to zero size; otherwise,2302* a new file will be created. The output will be written to the2303* file and is buffered.2304*2305* @param charset2306* A {@linkplain java.nio.charset.Charset charset}2307*2308* @param l2309* The {@linkplain java.util.Locale locale} to apply during2310* formatting. If {@code l} is {@code null} then no localization2311* is applied.2312*2313* @throws IOException2314* if an I/O error occurs while opening or creating the file2315*2316* @throws SecurityException2317* If a security manager is present and {@link2318* SecurityManager#checkWrite checkWrite(file.getPath())} denies2319* write access to the file2320*2321* @throws NullPointerException2322* if {@code file} or {@code charset} is {@code null}.2323*/2324public Formatter(File file, Charset charset, Locale l) throws IOException {2325this(Objects.requireNonNull(charset, "charset"), l, file);2326}232723282329/**2330* Constructs a new formatter with the specified print stream.2331*2332* <p> The locale used is the {@linkplain2333* Locale#getDefault(Locale.Category) default locale} for2334* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2335* virtual machine.2336*2337* <p> Characters are written to the given {@link java.io.PrintStream2338* PrintStream} object and are therefore encoded using that object's2339* charset.2340*2341* @param ps2342* The stream to use as the destination of this formatter.2343*/2344public Formatter(PrintStream ps) {2345this(Locale.getDefault(Locale.Category.FORMAT),2346(Appendable)Objects.requireNonNull(ps));2347}23482349/**2350* Constructs a new formatter with the specified output stream.2351*2352* <p> The charset used is the {@linkplain2353* java.nio.charset.Charset#defaultCharset() default charset} for this2354* instance of the Java virtual machine.2355*2356* <p> The locale used is the {@linkplain2357* Locale#getDefault(Locale.Category) default locale} for2358* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2359* virtual machine.2360*2361* @param os2362* The output stream to use as the destination of this formatter.2363* The output will be buffered.2364*/2365public Formatter(OutputStream os) {2366this(Locale.getDefault(Locale.Category.FORMAT),2367new BufferedWriter(new OutputStreamWriter(os)));2368}23692370/**2371* Constructs a new formatter with the specified output stream and2372* charset.2373*2374* <p> The locale used is the {@linkplain2375* Locale#getDefault(Locale.Category) default locale} for2376* {@linkplain Locale.Category#FORMAT formatting} for this instance of the Java2377* virtual machine.2378*2379* @param os2380* The output stream to use as the destination of this formatter.2381* The output will be buffered.2382*2383* @param csn2384* The name of a supported {@linkplain java.nio.charset.Charset2385* charset}2386*2387* @throws UnsupportedEncodingException2388* If the named charset is not supported2389*/2390public Formatter(OutputStream os, String csn)2391throws UnsupportedEncodingException2392{2393this(os, csn, Locale.getDefault(Locale.Category.FORMAT));2394}23952396/**2397* Constructs a new formatter with the specified output stream, charset,2398* and locale.2399*2400* @param os2401* The output stream to use as the destination of this formatter.2402* The output will be buffered.2403*2404* @param csn2405* The name of a supported {@linkplain java.nio.charset.Charset2406* charset}2407*2408* @param l2409* The {@linkplain java.util.Locale locale} to apply during2410* formatting. If {@code l} is {@code null} then no localization2411* is applied.2412*2413* @throws UnsupportedEncodingException2414* If the named charset is not supported2415*/2416public Formatter(OutputStream os, String csn, Locale l)2417throws UnsupportedEncodingException2418{2419this(l, new BufferedWriter(new OutputStreamWriter(os, csn)));2420}24212422/**2423* Constructs a new formatter with the specified output stream, charset,2424* and locale.2425*2426* @param os2427* The output stream to use as the destination of this formatter.2428* The output will be buffered.2429*2430* @param charset2431* A {@linkplain java.nio.charset.Charset charset}2432*2433* @param l2434* The {@linkplain java.util.Locale locale} to apply during2435* formatting. If {@code l} is {@code null} then no localization2436* is applied.2437*2438* @throws NullPointerException2439* if {@code os} or {@code charset} is {@code null}.2440*/2441public Formatter(OutputStream os, Charset charset, Locale l) {2442this(l, new BufferedWriter(new OutputStreamWriter(os, charset)));2443}24442445private char zero() {2446char zero = this.zero;2447if (zero == ZERO_SENTINEL) {2448if ((l != null) && !l.equals(Locale.US)) {2449DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);2450zero = dfs.getZeroDigit();2451} else {2452zero = '0';2453}2454this.zero = zero;2455}2456return zero;2457}24582459/**2460* Returns the locale set by the construction of this formatter.2461*2462* <p> The {@link #format(java.util.Locale,String,Object...) format} method2463* for this object which has a locale argument does not change this value.2464*2465* @return {@code null} if no localization is applied, otherwise a2466* locale2467*2468* @throws FormatterClosedException2469* If this formatter has been closed by invoking its {@link2470* #close()} method2471*/2472public Locale locale() {2473ensureOpen();2474return l;2475}24762477/**2478* Returns the destination for the output.2479*2480* @return The destination for the output2481*2482* @throws FormatterClosedException2483* If this formatter has been closed by invoking its {@link2484* #close()} method2485*/2486public Appendable out() {2487ensureOpen();2488return a;2489}24902491/**2492* Returns the result of invoking {@code toString()} on the destination2493* for the output. For example, the following code formats text into a2494* {@link StringBuilder} then retrieves the resultant string:2495*2496* <blockquote><pre>2497* Formatter f = new Formatter();2498* f.format("Last reboot at %tc", lastRebootDate);2499* String s = f.toString();2500* // -> s == "Last reboot at Sat Jan 01 00:00:00 PST 2000"2501* </pre></blockquote>2502*2503* <p> An invocation of this method behaves in exactly the same way as the2504* invocation2505*2506* <pre>2507* out().toString() </pre>2508*2509* <p> Depending on the specification of {@code toString} for the {@link2510* Appendable}, the returned string may or may not contain the characters2511* written to the destination. For instance, buffers typically return2512* their contents in {@code toString()}, but streams cannot since the2513* data is discarded.2514*2515* @return The result of invoking {@code toString()} on the destination2516* for the output2517*2518* @throws FormatterClosedException2519* If this formatter has been closed by invoking its {@link2520* #close()} method2521*/2522public String toString() {2523ensureOpen();2524return a.toString();2525}25262527/**2528* Flushes this formatter. If the destination implements the {@link2529* java.io.Flushable} interface, its {@code flush} method will be invoked.2530*2531* <p> Flushing a formatter writes any buffered output in the destination2532* to the underlying stream.2533*2534* @throws FormatterClosedException2535* If this formatter has been closed by invoking its {@link2536* #close()} method2537*/2538public void flush() {2539ensureOpen();2540if (a instanceof Flushable) {2541try {2542((Flushable)a).flush();2543} catch (IOException ioe) {2544lastException = ioe;2545}2546}2547}25482549/**2550* Closes this formatter. If the destination implements the {@link2551* java.io.Closeable} interface, its {@code close} method will be invoked.2552*2553* <p> Closing a formatter allows it to release resources it may be holding2554* (such as open files). If the formatter is already closed, then invoking2555* this method has no effect.2556*2557* <p> Attempting to invoke any methods except {@link #ioException()} in2558* this formatter after it has been closed will result in a {@link2559* FormatterClosedException}.2560*/2561public void close() {2562if (a == null)2563return;2564try {2565if (a instanceof Closeable)2566((Closeable)a).close();2567} catch (IOException ioe) {2568lastException = ioe;2569} finally {2570a = null;2571}2572}25732574private void ensureOpen() {2575if (a == null)2576throw new FormatterClosedException();2577}25782579/**2580* Returns the {@code IOException} last thrown by this formatter's {@link2581* Appendable}.2582*2583* <p> If the destination's {@code append()} method never throws2584* {@code IOException}, then this method will always return {@code null}.2585*2586* @return The last exception thrown by the Appendable or {@code null} if2587* no such exception exists.2588*/2589public IOException ioException() {2590return lastException;2591}25922593/**2594* Writes a formatted string to this object's destination using the2595* specified format string and arguments. The locale used is the one2596* defined during the construction of this formatter.2597*2598* @param format2599* A format string as described in <a href="#syntax">Format string2600* syntax</a>.2601*2602* @param args2603* Arguments referenced by the format specifiers in the format2604* string. If there are more arguments than format specifiers, the2605* extra arguments are ignored. The maximum number of arguments is2606* limited by the maximum dimension of a Java array as defined by2607* <cite>The Java Virtual Machine Specification</cite>.2608*2609* @throws IllegalFormatException2610* If a format string contains an illegal syntax, a format2611* specifier that is incompatible with the given arguments,2612* insufficient arguments given the format string, or other2613* illegal conditions. For specification of all possible2614* formatting errors, see the <a href="#detail">Details</a>2615* section of the formatter class specification.2616*2617* @throws FormatterClosedException2618* If this formatter has been closed by invoking its {@link2619* #close()} method2620*2621* @return This formatter2622*/2623public Formatter format(String format, Object ... args) {2624return format(l, format, args);2625}26262627/**2628* Writes a formatted string to this object's destination using the2629* specified locale, format string, and arguments.2630*2631* @param l2632* The {@linkplain java.util.Locale locale} to apply during2633* formatting. If {@code l} is {@code null} then no localization2634* is applied. This does not change this object's locale that was2635* set during construction.2636*2637* @param format2638* A format string as described in <a href="#syntax">Format string2639* syntax</a>2640*2641* @param args2642* Arguments referenced by the format specifiers in the format2643* string. If there are more arguments than format specifiers, the2644* extra arguments are ignored. The maximum number of arguments is2645* limited by the maximum dimension of a Java array as defined by2646* <cite>The Java Virtual Machine Specification</cite>.2647*2648* @throws IllegalFormatException2649* If a format string contains an illegal syntax, a format2650* specifier that is incompatible with the given arguments,2651* insufficient arguments given the format string, or other2652* illegal conditions. For specification of all possible2653* formatting errors, see the <a href="#detail">Details</a>2654* section of the formatter class specification.2655*2656* @throws FormatterClosedException2657* If this formatter has been closed by invoking its {@link2658* #close()} method2659*2660* @return This formatter2661*/2662public Formatter format(Locale l, String format, Object ... args) {2663ensureOpen();26642665// index of last argument referenced2666int last = -1;2667// last ordinary index2668int lasto = -1;26692670List<FormatString> fsa = parse(format);2671for (int i = 0; i < fsa.size(); i++) {2672var fs = fsa.get(i);2673int index = fs.index();2674try {2675switch (index) {2676case -2 -> // fixed string, "%n", or "%%"2677fs.print(null, l);2678case -1 -> { // relative index2679if (last < 0 || (args != null && last > args.length - 1))2680throw new MissingFormatArgumentException(fs.toString());2681fs.print((args == null ? null : args[last]), l);2682}2683case 0 -> { // ordinary index2684lasto++;2685last = lasto;2686if (args != null && lasto > args.length - 1)2687throw new MissingFormatArgumentException(fs.toString());2688fs.print((args == null ? null : args[lasto]), l);2689}2690default -> { // explicit index2691last = index - 1;2692if (args != null && last > args.length - 1)2693throw new MissingFormatArgumentException(fs.toString());2694fs.print((args == null ? null : args[last]), l);2695}2696}2697} catch (IOException x) {2698lastException = x;2699}2700}2701return this;2702}27032704// %[argument_index$][flags][width][.precision][t]conversion2705private static final String formatSpecifier2706= "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";27072708private static final Pattern fsPattern = Pattern.compile(formatSpecifier);27092710/**2711* Finds format specifiers in the format string.2712*/2713private List<FormatString> parse(String s) {2714ArrayList<FormatString> al = new ArrayList<>();2715int i = 0;2716int max = s.length();2717Matcher m = null; // create if needed2718while (i < max) {2719int n = s.indexOf('%', i);2720if (n < 0) {2721// No more format specifiers, but since2722// i < max there's some trailing text2723al.add(new FixedString(s, i, max));2724break;2725}2726if (i != n) {2727// Previous characters were fixed text2728al.add(new FixedString(s, i, n));2729}2730i = n + 1;2731if (i >= max) {2732// Trailing %2733throw new UnknownFormatConversionException("%");2734}2735char c = s.charAt(i);2736if (Conversion.isValid(c)) {2737al.add(new FormatSpecifier(c));2738i++;2739} else {2740if (m == null) {2741m = fsPattern.matcher(s);2742}2743// We have already parsed a '%' at n, so we either have a2744// match or the specifier at n is invalid2745if (m.find(n) && m.start() == n) {2746al.add(new FormatSpecifier(s, m));2747i = m.end();2748} else {2749throw new UnknownFormatConversionException(String.valueOf(c));2750}2751}2752}2753return al;2754}27552756private interface FormatString {2757int index();2758void print(Object arg, Locale l) throws IOException;2759String toString();2760}27612762private class FixedString implements FormatString {2763private final String s;2764private final int start;2765private final int end;2766FixedString(String s, int start, int end) {2767this.s = s;2768this.start = start;2769this.end = end;2770}2771public int index() { return -2; }2772public void print(Object arg, Locale l)2773throws IOException { a.append(s, start, end); }2774public String toString() { return s.substring(start, end); }2775}27762777/**2778* Enum for {@code BigDecimal} formatting.2779*/2780public enum BigDecimalLayoutForm {2781/**2782* Format the {@code BigDecimal} in computerized scientific notation.2783*/2784SCIENTIFIC,27852786/**2787* Format the {@code BigDecimal} as a decimal number.2788*/2789DECIMAL_FLOAT2790};27912792private class FormatSpecifier implements FormatString {27932794private int index = 0;2795private Flags f = Flags.NONE;2796private int width = -1;2797private int precision = -1;2798private boolean dt = false;2799private char c;28002801private void index(String s, int start, int end) {2802if (start >= 0) {2803try {2804// skip the trailing '$'2805index = Integer.parseInt(s, start, end - 1, 10);2806if (index <= 0) {2807throw new IllegalFormatArgumentIndexException(index);2808}2809} catch (NumberFormatException x) {2810throw new IllegalFormatArgumentIndexException(Integer.MIN_VALUE);2811}2812}2813}28142815public int index() {2816return index;2817}28182819private void flags(String s, int start, int end) {2820f = Flags.parse(s, start, end);2821if (f.contains(Flags.PREVIOUS))2822index = -1;2823}28242825private void width(String s, int start, int end) {2826if (start >= 0) {2827try {2828width = Integer.parseInt(s, start, end, 10);2829if (width < 0)2830throw new IllegalFormatWidthException(width);2831} catch (NumberFormatException x) {2832throw new IllegalFormatWidthException(Integer.MIN_VALUE);2833}2834}2835}28362837private void precision(String s, int start, int end) {2838if (start >= 0) {2839try {2840// skip the leading '.'2841precision = Integer.parseInt(s, start + 1, end, 10);2842if (precision < 0)2843throw new IllegalFormatPrecisionException(precision);2844} catch (NumberFormatException x) {2845throw new IllegalFormatPrecisionException(Integer.MIN_VALUE);2846}2847}2848}28492850private void conversion(char conv) {2851c = conv;2852if (!dt) {2853if (!Conversion.isValid(c)) {2854throw new UnknownFormatConversionException(String.valueOf(c));2855}2856if (Character.isUpperCase(c)) {2857f.add(Flags.UPPERCASE);2858c = Character.toLowerCase(c);2859}2860if (Conversion.isText(c)) {2861index = -2;2862}2863}2864}28652866FormatSpecifier(char conv) {2867c = conv;2868if (Character.isUpperCase(conv)) {2869f = Flags.UPPERCASE;2870c = Character.toLowerCase(conv);2871}2872if (Conversion.isText(conv)) {2873index = -2;2874}2875}28762877FormatSpecifier(String s, Matcher m) {2878index(s, m.start(1), m.end(1));2879flags(s, m.start(2), m.end(2));2880width(s, m.start(3), m.end(3));2881precision(s, m.start(4), m.end(4));28822883int tTStart = m.start(5);2884if (tTStart >= 0) {2885dt = true;2886if (s.charAt(tTStart) == 'T') {2887f.add(Flags.UPPERCASE);2888}2889}2890conversion(s.charAt(m.start(6)));28912892if (dt)2893checkDateTime();2894else if (Conversion.isGeneral(c))2895checkGeneral();2896else if (Conversion.isCharacter(c))2897checkCharacter();2898else if (Conversion.isInteger(c))2899checkInteger();2900else if (Conversion.isFloat(c))2901checkFloat();2902else if (Conversion.isText(c))2903checkText();2904else2905throw new UnknownFormatConversionException(String.valueOf(c));2906}29072908public void print(Object arg, Locale l) throws IOException {2909if (dt) {2910printDateTime(arg, l);2911return;2912}2913switch(c) {2914case Conversion.DECIMAL_INTEGER:2915case Conversion.OCTAL_INTEGER:2916case Conversion.HEXADECIMAL_INTEGER:2917printInteger(arg, l);2918break;2919case Conversion.SCIENTIFIC:2920case Conversion.GENERAL:2921case Conversion.DECIMAL_FLOAT:2922case Conversion.HEXADECIMAL_FLOAT:2923printFloat(arg, l);2924break;2925case Conversion.CHARACTER:2926printCharacter(arg, l);2927break;2928case Conversion.BOOLEAN:2929printBoolean(arg, l);2930break;2931case Conversion.STRING:2932printString(arg, l);2933break;2934case Conversion.HASHCODE:2935printHashCode(arg, l);2936break;2937case Conversion.LINE_SEPARATOR:2938a.append(System.lineSeparator());2939break;2940case Conversion.PERCENT_SIGN:2941print("%", l);2942break;2943default:2944assert false;2945}2946}29472948private void printInteger(Object arg, Locale l) throws IOException {2949if (arg == null)2950print("null", l);2951else if (arg instanceof Byte)2952print(((Byte)arg).byteValue(), l);2953else if (arg instanceof Short)2954print(((Short)arg).shortValue(), l);2955else if (arg instanceof Integer)2956print(((Integer)arg).intValue(), l);2957else if (arg instanceof Long)2958print(((Long)arg).longValue(), l);2959else if (arg instanceof BigInteger)2960print(((BigInteger)arg), l);2961else2962failConversion(c, arg);2963}29642965private void printFloat(Object arg, Locale l) throws IOException {2966if (arg == null)2967print("null", l);2968else if (arg instanceof Float)2969print(((Float)arg).floatValue(), l);2970else if (arg instanceof Double)2971print(((Double)arg).doubleValue(), l);2972else if (arg instanceof BigDecimal)2973print(((BigDecimal)arg), l);2974else2975failConversion(c, arg);2976}29772978private void printDateTime(Object arg, Locale l) throws IOException {2979if (arg == null) {2980print("null", l);2981return;2982}2983Calendar cal = null;29842985// Instead of Calendar.setLenient(true), perhaps we should2986// wrap the IllegalArgumentException that might be thrown?2987if (arg instanceof Long) {2988// Note that the following method uses an instance of the2989// default time zone (TimeZone.getDefaultRef().2990cal = Calendar.getInstance(l == null ? Locale.US : l);2991cal.setTimeInMillis((Long)arg);2992} else if (arg instanceof Date) {2993// Note that the following method uses an instance of the2994// default time zone (TimeZone.getDefaultRef().2995cal = Calendar.getInstance(l == null ? Locale.US : l);2996cal.setTime((Date)arg);2997} else if (arg instanceof Calendar) {2998cal = (Calendar) ((Calendar) arg).clone();2999cal.setLenient(true);3000} else if (arg instanceof TemporalAccessor) {3001print((TemporalAccessor) arg, c, l);3002return;3003} else {3004failConversion(c, arg);3005}3006// Use the provided locale so that invocations of3007// localizedMagnitude() use optimizations for null.3008print(cal, c, l);3009}30103011private void printCharacter(Object arg, Locale l) throws IOException {3012if (arg == null) {3013print("null", l);3014return;3015}3016String s = null;3017if (arg instanceof Character) {3018s = ((Character)arg).toString();3019} else if (arg instanceof Byte) {3020byte i = (Byte) arg;3021if (Character.isValidCodePoint(i))3022s = new String(Character.toChars(i));3023else3024throw new IllegalFormatCodePointException(i);3025} else if (arg instanceof Short) {3026short i = (Short) arg;3027if (Character.isValidCodePoint(i))3028s = new String(Character.toChars(i));3029else3030throw new IllegalFormatCodePointException(i);3031} else if (arg instanceof Integer) {3032int i = (Integer) arg;3033if (Character.isValidCodePoint(i))3034s = new String(Character.toChars(i));3035else3036throw new IllegalFormatCodePointException(i);3037} else {3038failConversion(c, arg);3039}3040print(s, l);3041}30423043private void printString(Object arg, Locale l) throws IOException {3044if (arg instanceof Formattable) {3045Formatter fmt = Formatter.this;3046if (fmt.locale() != l)3047fmt = new Formatter(fmt.out(), l);3048((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);3049} else {3050if (f.contains(Flags.ALTERNATE))3051failMismatch(Flags.ALTERNATE, 's');3052if (arg == null)3053print("null", l);3054else3055print(arg.toString(), l);3056}3057}30583059private void printBoolean(Object arg, Locale l) throws IOException {3060String s;3061if (arg != null)3062s = ((arg instanceof Boolean)3063? ((Boolean)arg).toString()3064: Boolean.toString(true));3065else3066s = Boolean.toString(false);3067print(s, l);3068}30693070private void printHashCode(Object arg, Locale l) throws IOException {3071String s = (arg == null3072? "null"3073: Integer.toHexString(arg.hashCode()));3074print(s, l);3075}30763077private void print(String s, Locale l) throws IOException {3078if (precision != -1 && precision < s.length())3079s = s.substring(0, precision);3080if (f.contains(Flags.UPPERCASE))3081s = toUpperCaseWithLocale(s, l);3082appendJustified(a, s);3083}30843085private String toUpperCaseWithLocale(String s, Locale l) {3086return s.toUpperCase(Objects.requireNonNullElse(l,3087Locale.getDefault(Locale.Category.FORMAT)));3088}30893090private void appendJustified(Appendable a, CharSequence cs) throws IOException {3091if (width == -1) {3092a.append(cs);3093return;3094}3095boolean padRight = f.contains(Flags.LEFT_JUSTIFY);3096int sp = width - cs.length();3097if (padRight) {3098a.append(cs);3099}3100for (int i = 0; i < sp; i++) {3101a.append(' ');3102}3103if (!padRight) {3104a.append(cs);3105}3106}31073108public String toString() {3109StringBuilder sb = new StringBuilder("%");3110// Flags.UPPERCASE is set internally for legal conversions.3111Flags dupf = f.dup().remove(Flags.UPPERCASE);3112sb.append(dupf.toString());3113if (index > 0)3114sb.append(index).append('$');3115if (width != -1)3116sb.append(width);3117if (precision != -1)3118sb.append('.').append(precision);3119if (dt)3120sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');3121sb.append(f.contains(Flags.UPPERCASE)3122? Character.toUpperCase(c) : c);3123return sb.toString();3124}31253126private void checkGeneral() {3127if ((c == Conversion.BOOLEAN || c == Conversion.HASHCODE)3128&& f.contains(Flags.ALTERNATE))3129failMismatch(Flags.ALTERNATE, c);3130// '-' requires a width3131if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))3132throw new MissingFormatWidthException(toString());3133checkBadFlags(Flags.PLUS, Flags.LEADING_SPACE, Flags.ZERO_PAD,3134Flags.GROUP, Flags.PARENTHESES);3135}31363137private void checkDateTime() {3138if (precision != -1)3139throw new IllegalFormatPrecisionException(precision);3140if (!DateTime.isValid(c))3141throw new UnknownFormatConversionException("t" + c);3142checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,3143Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);3144// '-' requires a width3145if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))3146throw new MissingFormatWidthException(toString());3147}31483149private void checkCharacter() {3150if (precision != -1)3151throw new IllegalFormatPrecisionException(precision);3152checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,3153Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);3154// '-' requires a width3155if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))3156throw new MissingFormatWidthException(toString());3157}31583159private void checkInteger() {3160checkNumeric();3161if (precision != -1)3162throw new IllegalFormatPrecisionException(precision);31633164if (c == Conversion.DECIMAL_INTEGER)3165checkBadFlags(Flags.ALTERNATE);3166else if (c == Conversion.OCTAL_INTEGER)3167checkBadFlags(Flags.GROUP);3168else3169checkBadFlags(Flags.GROUP);3170}31713172private void checkBadFlags(Flags ... badFlags) {3173for (Flags badFlag : badFlags)3174if (f.contains(badFlag))3175failMismatch(badFlag, c);3176}31773178private void checkFloat() {3179checkNumeric();3180if (c == Conversion.DECIMAL_FLOAT) {3181} else if (c == Conversion.HEXADECIMAL_FLOAT) {3182checkBadFlags(Flags.PARENTHESES, Flags.GROUP);3183} else if (c == Conversion.SCIENTIFIC) {3184checkBadFlags(Flags.GROUP);3185} else if (c == Conversion.GENERAL) {3186checkBadFlags(Flags.ALTERNATE);3187}3188}31893190private void checkNumeric() {3191if (width != -1 && width < 0)3192throw new IllegalFormatWidthException(width);31933194if (precision != -1 && precision < 0)3195throw new IllegalFormatPrecisionException(precision);31963197// '-' and '0' require a width3198if (width == -13199&& (f.contains(Flags.LEFT_JUSTIFY) || f.contains(Flags.ZERO_PAD)))3200throw new MissingFormatWidthException(toString());32013202// bad combination3203if ((f.contains(Flags.PLUS) && f.contains(Flags.LEADING_SPACE))3204|| (f.contains(Flags.LEFT_JUSTIFY) && f.contains(Flags.ZERO_PAD)))3205throw new IllegalFormatFlagsException(f.toString());3206}32073208private void checkText() {3209if (precision != -1)3210throw new IllegalFormatPrecisionException(precision);3211switch (c) {3212case Conversion.PERCENT_SIGN:3213if (f.valueOf() != Flags.LEFT_JUSTIFY.valueOf()3214&& f.valueOf() != Flags.NONE.valueOf())3215throw new IllegalFormatFlagsException(f.toString());3216// '-' requires a width3217if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))3218throw new MissingFormatWidthException(toString());3219break;3220case Conversion.LINE_SEPARATOR:3221if (width != -1)3222throw new IllegalFormatWidthException(width);3223if (f.valueOf() != Flags.NONE.valueOf())3224throw new IllegalFormatFlagsException(f.toString());3225break;3226default:3227assert false;3228}3229}32303231private void print(byte value, Locale l) throws IOException {3232long v = value;3233if (value < 03234&& (c == Conversion.OCTAL_INTEGER3235|| c == Conversion.HEXADECIMAL_INTEGER)) {3236v += (1L << 8);3237}3238print(v, l);3239}32403241private void print(short value, Locale l) throws IOException {3242long v = value;3243if (value < 03244&& (c == Conversion.OCTAL_INTEGER3245|| c == Conversion.HEXADECIMAL_INTEGER)) {3246v += (1L << 16);3247assert v >= 0 : v;3248}3249print(v, l);3250}32513252private void print(int value, Locale l) throws IOException {3253long v = value;3254if (value < 03255&& (c == Conversion.OCTAL_INTEGER3256|| c == Conversion.HEXADECIMAL_INTEGER)) {3257v += (1L << 32);3258assert v >= 0 : v;3259}3260print(v, l);3261}32623263private void print(long value, Locale l) throws IOException {32643265StringBuilder sb = new StringBuilder();32663267if (c == Conversion.DECIMAL_INTEGER) {3268boolean neg = value < 0;3269String valueStr = Long.toString(value, 10);32703271// leading sign indicator3272leadingSign(sb, neg);32733274// the value3275localizedMagnitude(sb, valueStr, neg ? 1 : 0, f, adjustWidth(width, f, neg), l);32763277// trailing sign indicator3278trailingSign(sb, neg);3279} else if (c == Conversion.OCTAL_INTEGER) {3280checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,3281Flags.PLUS);3282String s = Long.toOctalString(value);3283int len = (f.contains(Flags.ALTERNATE)3284? s.length() + 13285: s.length());32863287// apply ALTERNATE (radix indicator for octal) before ZERO_PAD3288if (f.contains(Flags.ALTERNATE))3289sb.append('0');3290if (f.contains(Flags.ZERO_PAD)) {3291trailingZeros(sb, width - len);3292}3293sb.append(s);3294} else if (c == Conversion.HEXADECIMAL_INTEGER) {3295checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,3296Flags.PLUS);3297String s = Long.toHexString(value);3298int len = (f.contains(Flags.ALTERNATE)3299? s.length() + 23300: s.length());33013302// apply ALTERNATE (radix indicator for hex) before ZERO_PAD3303if (f.contains(Flags.ALTERNATE))3304sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");3305if (f.contains(Flags.ZERO_PAD)) {3306trailingZeros(sb, width - len);3307}3308if (f.contains(Flags.UPPERCASE))3309s = toUpperCaseWithLocale(s, l);3310sb.append(s);3311}33123313// justify based on width3314appendJustified(a, sb);3315}33163317// neg := val < 03318private StringBuilder leadingSign(StringBuilder sb, boolean neg) {3319if (!neg) {3320if (f.contains(Flags.PLUS)) {3321sb.append('+');3322} else if (f.contains(Flags.LEADING_SPACE)) {3323sb.append(' ');3324}3325} else {3326if (f.contains(Flags.PARENTHESES))3327sb.append('(');3328else3329sb.append('-');3330}3331return sb;3332}33333334// neg := val < 03335private StringBuilder trailingSign(StringBuilder sb, boolean neg) {3336if (neg && f.contains(Flags.PARENTHESES))3337sb.append(')');3338return sb;3339}33403341private void print(BigInteger value, Locale l) throws IOException {3342StringBuilder sb = new StringBuilder();3343boolean neg = value.signum() == -1;3344BigInteger v = value.abs();33453346// leading sign indicator3347leadingSign(sb, neg);33483349// the value3350if (c == Conversion.DECIMAL_INTEGER) {3351localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);3352} else if (c == Conversion.OCTAL_INTEGER) {3353String s = v.toString(8);33543355int len = s.length() + sb.length();3356if (neg && f.contains(Flags.PARENTHESES))3357len++;33583359// apply ALTERNATE (radix indicator for octal) before ZERO_PAD3360if (f.contains(Flags.ALTERNATE)) {3361len++;3362sb.append('0');3363}3364if (f.contains(Flags.ZERO_PAD)) {3365trailingZeros(sb, width - len);3366}3367sb.append(s);3368} else if (c == Conversion.HEXADECIMAL_INTEGER) {3369String s = v.toString(16);33703371int len = s.length() + sb.length();3372if (neg && f.contains(Flags.PARENTHESES))3373len++;33743375// apply ALTERNATE (radix indicator for hex) before ZERO_PAD3376if (f.contains(Flags.ALTERNATE)) {3377len += 2;3378sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");3379}3380if (f.contains(Flags.ZERO_PAD)) {3381trailingZeros(sb, width - len);3382}3383if (f.contains(Flags.UPPERCASE))3384s = toUpperCaseWithLocale(s, l);3385sb.append(s);3386}33873388// trailing sign indicator3389trailingSign(sb, (value.signum() == -1));33903391// justify based on width3392appendJustified(a, sb);3393}33943395private void print(float value, Locale l) throws IOException {3396print((double) value, l);3397}33983399private void print(double value, Locale l) throws IOException {3400StringBuilder sb = new StringBuilder();3401boolean neg = Double.compare(value, 0.0) == -1;34023403if (!Double.isNaN(value)) {3404double v = Math.abs(value);34053406// leading sign indicator3407leadingSign(sb, neg);34083409// the value3410if (!Double.isInfinite(v))3411print(sb, v, l, f, c, precision, neg);3412else3413sb.append(f.contains(Flags.UPPERCASE)3414? "INFINITY" : "Infinity");34153416// trailing sign indicator3417trailingSign(sb, neg);3418} else {3419sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");3420}34213422// justify based on width3423appendJustified(a, sb);3424}34253426// !Double.isInfinite(value) && !Double.isNaN(value)3427private void print(StringBuilder sb, double value, Locale l,3428Flags f, char c, int precision, boolean neg)3429throws IOException3430{3431if (c == Conversion.SCIENTIFIC) {3432// Create a new FormattedFloatingDecimal with the desired3433// precision.3434int prec = (precision == -1 ? 6 : precision);34353436FormattedFloatingDecimal fd3437= FormattedFloatingDecimal.valueOf(value, prec,3438FormattedFloatingDecimal.Form.SCIENTIFIC);34393440StringBuilder mant = new StringBuilder().append(fd.getMantissa());3441addZeros(mant, prec);34423443// If the precision is zero and the '#' flag is set, add the3444// requested decimal point.3445if (f.contains(Flags.ALTERNATE) && (prec == 0)) {3446mant.append('.');3447}34483449char[] exp = (value == 0.0)3450? new char[] {'+','0','0'} : fd.getExponent();34513452int newW = width;3453if (width != -1) {3454newW = adjustWidth(width - exp.length - 1, f, neg);3455}3456localizedMagnitude(sb, mant, 0, f, newW, l);34573458sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');34593460char sign = exp[0];3461assert(sign == '+' || sign == '-');3462sb.append(sign);34633464localizedMagnitudeExp(sb, exp, 1, l);3465} else if (c == Conversion.DECIMAL_FLOAT) {3466// Create a new FormattedFloatingDecimal with the desired3467// precision.3468int prec = (precision == -1 ? 6 : precision);34693470FormattedFloatingDecimal fd3471= FormattedFloatingDecimal.valueOf(value, prec,3472FormattedFloatingDecimal.Form.DECIMAL_FLOAT);34733474StringBuilder mant = new StringBuilder().append(fd.getMantissa());3475addZeros(mant, prec);34763477// If the precision is zero and the '#' flag is set, add the3478// requested decimal point.3479if (f.contains(Flags.ALTERNATE) && (prec == 0))3480mant.append('.');34813482int newW = width;3483if (width != -1)3484newW = adjustWidth(width, f, neg);3485localizedMagnitude(sb, mant, 0, f, newW, l);3486} else if (c == Conversion.GENERAL) {3487int prec = precision;3488if (precision == -1)3489prec = 6;3490else if (precision == 0)3491prec = 1;34923493char[] exp;3494StringBuilder mant = new StringBuilder();3495int expRounded;3496if (value == 0.0) {3497exp = null;3498mant.append('0');3499expRounded = 0;3500} else {3501FormattedFloatingDecimal fd3502= FormattedFloatingDecimal.valueOf(value, prec,3503FormattedFloatingDecimal.Form.GENERAL);3504exp = fd.getExponent();3505mant.append(fd.getMantissa());3506expRounded = fd.getExponentRounded();3507}35083509if (exp != null) {3510prec -= 1;3511} else {3512prec -= expRounded + 1;3513}35143515addZeros(mant, prec);3516// If the precision is zero and the '#' flag is set, add the3517// requested decimal point.3518if (f.contains(Flags.ALTERNATE) && (prec == 0)) {3519mant.append('.');3520}35213522int newW = width;3523if (width != -1) {3524if (exp != null)3525newW = adjustWidth(width - exp.length - 1, f, neg);3526else3527newW = adjustWidth(width, f, neg);3528}3529localizedMagnitude(sb, mant, 0, f, newW, l);35303531if (exp != null) {3532sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');35333534char sign = exp[0];3535assert(sign == '+' || sign == '-');3536sb.append(sign);35373538localizedMagnitudeExp(sb, exp, 1, l);3539}3540} else if (c == Conversion.HEXADECIMAL_FLOAT) {3541int prec = precision;3542if (precision == -1)3543// assume that we want all of the digits3544prec = 0;3545else if (precision == 0)3546prec = 1;35473548String s = hexDouble(value, prec);35493550StringBuilder va = new StringBuilder();3551boolean upper = f.contains(Flags.UPPERCASE);3552sb.append(upper ? "0X" : "0x");35533554if (f.contains(Flags.ZERO_PAD)) {3555int leadingCharacters = 2;3556if(f.contains(Flags.LEADING_SPACE) ||3557f.contains(Flags.PLUS) || neg) {3558leadingCharacters = 3;3559}3560trailingZeros(sb, width - s.length() - leadingCharacters);3561}35623563int idx = s.indexOf('p');3564if (upper) {3565String tmp = s.substring(0, idx);3566// don't localize hex3567tmp = tmp.toUpperCase(Locale.ROOT);3568va.append(tmp);3569} else {3570va.append(s, 0, idx);3571}3572if (prec != 0) {3573addZeros(va, prec);3574}3575sb.append(va);3576sb.append(upper ? 'P' : 'p');3577sb.append(s, idx+1, s.length());3578}3579}35803581// Add zeros to the requested precision.3582private void addZeros(StringBuilder sb, int prec) {3583// Look for the dot. If we don't find one, the we'll need to add3584// it before we add the zeros.3585int len = sb.length();3586int i;3587for (i = 0; i < len; i++) {3588if (sb.charAt(i) == '.') {3589break;3590}3591}3592boolean needDot = false;3593if (i == len) {3594needDot = true;3595}35963597// Determine existing precision.3598int outPrec = len - i - (needDot ? 0 : 1);3599assert (outPrec <= prec);3600if (outPrec == prec) {3601return;3602}36033604// Add dot if previously determined to be necessary.3605if (needDot) {3606sb.append('.');3607}36083609// Add zeros.3610trailingZeros(sb, prec - outPrec);3611}36123613// Method assumes that d > 0.3614private String hexDouble(double d, int prec) {3615// Let Double.toHexString handle simple cases3616if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {3617// remove "0x"3618return Double.toHexString(d).substring(2);3619} else {3620assert(prec >= 1 && prec <= 12);36213622int exponent = Math.getExponent(d);3623boolean subnormal3624= (exponent == Double.MIN_EXPONENT - 1);36253626// If this is subnormal input so normalize (could be faster to3627// do as integer operation).3628if (subnormal) {3629double scaleUp = Math.scalb(1.0, 54);3630d *= scaleUp;3631// Calculate the exponent. This is not just exponent + 543632// since the former is not the normalized exponent.3633exponent = Math.getExponent(d);3634assert exponent >= Double.MIN_EXPONENT &&3635exponent <= Double.MAX_EXPONENT: exponent;3636}36373638int precision = 1 + prec*4;3639int shiftDistance3640= DoubleConsts.SIGNIFICAND_WIDTH - precision;3641assert(shiftDistance >= 1 && shiftDistance < DoubleConsts.SIGNIFICAND_WIDTH);36423643long doppel = Double.doubleToLongBits(d);3644// Deterime the number of bits to keep.3645long newSignif3646= (doppel & (DoubleConsts.EXP_BIT_MASK3647| DoubleConsts.SIGNIF_BIT_MASK))3648>> shiftDistance;3649// Bits to round away.3650long roundingBits = doppel & ~(~0L << shiftDistance);36513652// To decide how to round, look at the low-order bit of the3653// working significand, the highest order discarded bit (the3654// round bit) and whether any of the lower order discarded bits3655// are nonzero (the sticky bit).36563657boolean leastZero = (newSignif & 0x1L) == 0L;3658boolean round3659= ((1L << (shiftDistance - 1) ) & roundingBits) != 0L;3660boolean sticky = shiftDistance > 1 &&3661(~(1L<< (shiftDistance - 1)) & roundingBits) != 0;3662if((leastZero && round && sticky) || (!leastZero && round)) {3663newSignif++;3664}36653666long signBit = doppel & DoubleConsts.SIGN_BIT_MASK;3667newSignif = signBit | (newSignif << shiftDistance);3668double result = Double.longBitsToDouble(newSignif);36693670if (Double.isInfinite(result) ) {3671// Infinite result generated by rounding3672return "1.0p1024";3673} else {3674String res = Double.toHexString(result).substring(2);3675if (!subnormal)3676return res;3677else {3678// Create a normalized subnormal string.3679int idx = res.indexOf('p');3680if (idx == -1) {3681// No 'p' character in hex string.3682assert false;3683return null;3684} else {3685// Get exponent and append at the end.3686String exp = res.substring(idx + 1);3687int iexp = Integer.parseInt(exp) -54;3688return res.substring(0, idx) + "p"3689+ Integer.toString(iexp);3690}3691}3692}3693}3694}36953696private void print(BigDecimal value, Locale l) throws IOException {3697if (c == Conversion.HEXADECIMAL_FLOAT)3698failConversion(c, value);3699StringBuilder sb = new StringBuilder();3700boolean neg = value.signum() == -1;3701BigDecimal v = value.abs();3702// leading sign indicator3703leadingSign(sb, neg);37043705// the value3706print(sb, v, l, f, c, precision, neg);37073708// trailing sign indicator3709trailingSign(sb, neg);37103711// justify based on width3712appendJustified(a, sb);3713}37143715// value > 03716private void print(StringBuilder sb, BigDecimal value, Locale l,3717Flags f, char c, int precision, boolean neg)3718throws IOException3719{3720if (c == Conversion.SCIENTIFIC) {3721// Create a new BigDecimal with the desired precision.3722int prec = (precision == -1 ? 6 : precision);3723int scale = value.scale();3724int origPrec = value.precision();3725int nzeros = 0;3726int compPrec;37273728if (prec > origPrec - 1) {3729compPrec = origPrec;3730nzeros = prec - (origPrec - 1);3731} else {3732compPrec = prec + 1;3733}37343735MathContext mc = new MathContext(compPrec);3736BigDecimal v3737= new BigDecimal(value.unscaledValue(), scale, mc);37383739BigDecimalLayout bdl3740= new BigDecimalLayout(v.unscaledValue(), v.scale(),3741BigDecimalLayoutForm.SCIENTIFIC);37423743StringBuilder mant = bdl.mantissa();37443745// Add a decimal point if necessary. The mantissa may not3746// contain a decimal point if the scale is zero (the internal3747// representation has no fractional part) or the original3748// precision is one. Append a decimal point if '#' is set or if3749// we require zero padding to get to the requested precision.3750if ((origPrec == 1 || !bdl.hasDot())3751&& (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {3752mant.append('.');3753}37543755// Add trailing zeros in the case precision is greater than3756// the number of available digits after the decimal separator.3757trailingZeros(mant, nzeros);37583759StringBuilder exp = bdl.exponent();3760int newW = width;3761if (width != -1) {3762newW = adjustWidth(width - exp.length() - 1, f, neg);3763}3764localizedMagnitude(sb, mant, 0, f, newW, l);37653766sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');37673768Flags flags = f.dup().remove(Flags.GROUP);3769char sign = exp.charAt(0);3770assert(sign == '+' || sign == '-');3771sb.append(sign);37723773sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));3774} else if (c == Conversion.DECIMAL_FLOAT) {3775// Create a new BigDecimal with the desired precision.3776int prec = (precision == -1 ? 6 : precision);3777int scale = value.scale();37783779if (scale > prec) {3780// more "scale" digits than the requested "precision"3781int compPrec = value.precision();3782if (compPrec <= scale) {3783// case of 0.xxxxxx3784value = value.setScale(prec, RoundingMode.HALF_UP);3785} else {3786compPrec -= (scale - prec);3787value = new BigDecimal(value.unscaledValue(),3788scale,3789new MathContext(compPrec));3790}3791}3792BigDecimalLayout bdl = new BigDecimalLayout(3793value.unscaledValue(),3794value.scale(),3795BigDecimalLayoutForm.DECIMAL_FLOAT);37963797StringBuilder mant = bdl.mantissa();3798int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);37993800// Add a decimal point if necessary. The mantissa may not3801// contain a decimal point if the scale is zero (the internal3802// representation has no fractional part). Append a decimal3803// point if '#' is set or we require zero padding to get to the3804// requested precision.3805if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)3806|| nzeros > 0)) {3807mant.append('.');3808}38093810// Add trailing zeros if the precision is greater than the3811// number of available digits after the decimal separator.3812trailingZeros(mant, nzeros);38133814localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);3815} else if (c == Conversion.GENERAL) {3816int prec = precision;3817if (precision == -1)3818prec = 6;3819else if (precision == 0)3820prec = 1;38213822value = value.round(new MathContext(prec));3823if ((value.equals(BigDecimal.ZERO))3824|| ((value.compareTo(BigDecimal.valueOf(1, 4)) != -1)3825&& (value.compareTo(BigDecimal.valueOf(1, -prec)) == -1))) {38263827int e = - value.scale()3828+ (value.unscaledValue().toString().length() - 1);38293830// xxx.yyy3831// g precision (# sig digits) = #x + #y3832// f precision = #y3833// exponent = #x - 13834// => f precision = g precision - exponent - 13835// 0.000zzz3836// g precision (# sig digits) = #z3837// f precision = #0 (after '.') + #z3838// exponent = - #0 (after '.') - 13839// => f precision = g precision - exponent - 13840prec = prec - e - 1;38413842print(sb, value, l, f, Conversion.DECIMAL_FLOAT, prec,3843neg);3844} else {3845print(sb, value, l, f, Conversion.SCIENTIFIC, prec - 1, neg);3846}3847} else if (c == Conversion.HEXADECIMAL_FLOAT) {3848// This conversion isn't supported. The error should be3849// reported earlier.3850assert false;3851}3852}38533854private class BigDecimalLayout {3855private StringBuilder mant;3856private StringBuilder exp;3857private boolean dot = false;3858private int scale;38593860public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {3861layout(intVal, scale, form);3862}38633864public boolean hasDot() {3865return dot;3866}38673868public int scale() {3869return scale;3870}38713872public StringBuilder mantissa() {3873return mant;3874}38753876// The exponent will be formatted as a sign ('+' or '-') followed3877// by the exponent zero-padded to include at least two digits.3878public StringBuilder exponent() {3879return exp;3880}38813882private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {3883String coeff = intVal.toString();3884this.scale = scale;38853886// Construct a buffer, with sufficient capacity for all cases.3887// If E-notation is needed, length will be: +1 if negative, +13888// if '.' needed, +2 for "E+", + up to 10 for adjusted3889// exponent. Otherwise it could have +1 if negative, plus3890// leading "0.00000"3891int len = coeff.length();3892mant = new StringBuilder(len + 14);38933894if (scale == 0) {3895if (len > 1) {3896mant.append(coeff.charAt(0));3897if (form == BigDecimalLayoutForm.SCIENTIFIC) {3898mant.append('.');3899dot = true;3900mant.append(coeff, 1, len);3901exp = new StringBuilder("+");3902if (len < 10) {3903exp.append('0').append(len - 1);3904} else {3905exp.append(len - 1);3906}3907} else {3908mant.append(coeff, 1, len);3909}3910} else {3911mant.append(coeff);3912if (form == BigDecimalLayoutForm.SCIENTIFIC) {3913exp = new StringBuilder("+00");3914}3915}3916} else if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {3917// count of padding zeros39183919if (scale >= len) {3920// 0.xxx form3921mant.append("0.");3922dot = true;3923trailingZeros(mant, scale - len);3924mant.append(coeff);3925} else {3926if (scale > 0) {3927// xx.xx form3928int pad = len - scale;3929mant.append(coeff, 0, pad);3930mant.append('.');3931dot = true;3932mant.append(coeff, pad, len);3933} else { // scale < 03934// xx form3935mant.append(coeff, 0, len);3936if (intVal.signum() != 0) {3937trailingZeros(mant, -scale);3938}3939this.scale = 0;3940}3941}3942} else {3943// x.xxx form3944mant.append(coeff.charAt(0));3945if (len > 1) {3946mant.append('.');3947dot = true;3948mant.append(coeff, 1, len);3949}3950exp = new StringBuilder();3951long adjusted = -(long) scale + (len - 1);3952if (adjusted != 0) {3953long abs = Math.abs(adjusted);3954// require sign3955exp.append(adjusted < 0 ? '-' : '+');3956if (abs < 10) {3957exp.append('0');3958}3959exp.append(abs);3960} else {3961exp.append("+00");3962}3963}3964}3965}39663967private int adjustWidth(int width, Flags f, boolean neg) {3968int newW = width;3969if (newW != -1 && neg && f.contains(Flags.PARENTHESES))3970newW--;3971return newW;3972}39733974// Add trailing zeros3975private void trailingZeros(StringBuilder sb, int nzeros) {3976for (int i = 0; i < nzeros; i++) {3977sb.append('0');3978}3979}39803981private void print(Calendar t, char c, Locale l) throws IOException {3982StringBuilder sb = new StringBuilder();3983print(sb, t, c, l);39843985// justify based on width3986if (f.contains(Flags.UPPERCASE)) {3987appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));3988} else {3989appendJustified(a, sb);3990}3991}39923993private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)3994throws IOException {3995if (sb == null)3996sb = new StringBuilder();3997switch (c) {3998case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)3999case DateTime.HOUR_0: // 'I' (01 - 12)4000case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H4001case DateTime.HOUR: { // 'l' (1 - 12) -- like I4002int i = t.get(Calendar.HOUR_OF_DAY);4003if (c == DateTime.HOUR_0 || c == DateTime.HOUR)4004i = (i == 0 || i == 12 ? 12 : i % 12);4005Flags flags = (c == DateTime.HOUR_OF_DAY_04006|| c == DateTime.HOUR_04007? Flags.ZERO_PAD4008: Flags.NONE);4009sb.append(localizedMagnitude(null, i, flags, 2, l));4010break;4011}4012case DateTime.MINUTE: { // 'M' (00 - 59)4013int i = t.get(Calendar.MINUTE);4014Flags flags = Flags.ZERO_PAD;4015sb.append(localizedMagnitude(null, i, flags, 2, l));4016break;4017}4018case DateTime.NANOSECOND: { // 'N' (000000000 - 999999999)4019int i = t.get(Calendar.MILLISECOND) * 1000000;4020Flags flags = Flags.ZERO_PAD;4021sb.append(localizedMagnitude(null, i, flags, 9, l));4022break;4023}4024case DateTime.MILLISECOND: { // 'L' (000 - 999)4025int i = t.get(Calendar.MILLISECOND);4026Flags flags = Flags.ZERO_PAD;4027sb.append(localizedMagnitude(null, i, flags, 3, l));4028break;4029}4030case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)4031long i = t.getTimeInMillis();4032Flags flags = Flags.NONE;4033sb.append(localizedMagnitude(null, i, flags, width, l));4034break;4035}4036case DateTime.AM_PM: { // 'p' (am or pm)4037// Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper4038String[] ampm = { "AM", "PM" };4039if (l != null && l != Locale.US) {4040DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);4041ampm = dfs.getAmPmStrings();4042}4043String s = ampm[t.get(Calendar.AM_PM)];4044sb.append(s.toLowerCase(Objects.requireNonNullElse(l,4045Locale.getDefault(Locale.Category.FORMAT))));4046break;4047}4048case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)4049long i = t.getTimeInMillis() / 1000;4050Flags flags = Flags.NONE;4051sb.append(localizedMagnitude(null, i, flags, width, l));4052break;4053}4054case DateTime.SECOND: { // 'S' (00 - 60 - leap second)4055int i = t.get(Calendar.SECOND);4056Flags flags = Flags.ZERO_PAD;4057sb.append(localizedMagnitude(null, i, flags, 2, l));4058break;4059}4060case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?4061int i = t.get(Calendar.ZONE_OFFSET) + t.get(Calendar.DST_OFFSET);4062boolean neg = i < 0;4063sb.append(neg ? '-' : '+');4064if (neg)4065i = -i;4066int min = i / 60000;4067// combine minute and hour into a single integer4068int offset = (min / 60) * 100 + (min % 60);4069Flags flags = Flags.ZERO_PAD;40704071sb.append(localizedMagnitude(null, offset, flags, 4, l));4072break;4073}4074case DateTime.ZONE: { // 'Z' (symbol)4075TimeZone tz = t.getTimeZone();4076sb.append(tz.getDisplayName((t.get(Calendar.DST_OFFSET) != 0),4077TimeZone.SHORT,4078Objects.requireNonNullElse(l, Locale.US)));4079break;4080}40814082// Date4083case DateTime.NAME_OF_DAY_ABBREV: // 'a'4084case DateTime.NAME_OF_DAY: { // 'A'4085int i = t.get(Calendar.DAY_OF_WEEK);4086Locale lt = Objects.requireNonNullElse(l, Locale.US);4087DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);4088if (c == DateTime.NAME_OF_DAY)4089sb.append(dfs.getWeekdays()[i]);4090else4091sb.append(dfs.getShortWeekdays()[i]);4092break;4093}4094case DateTime.NAME_OF_MONTH_ABBREV: // 'b'4095case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b4096case DateTime.NAME_OF_MONTH: { // 'B'4097int i = t.get(Calendar.MONTH);4098Locale lt = Objects.requireNonNullElse(l, Locale.US);4099DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);4100if (c == DateTime.NAME_OF_MONTH)4101sb.append(dfs.getMonths()[i]);4102else4103sb.append(dfs.getShortMonths()[i]);4104break;4105}4106case DateTime.CENTURY: // 'C' (00 - 99)4107case DateTime.YEAR_2: // 'y' (00 - 99)4108case DateTime.YEAR_4: { // 'Y' (0000 - 9999)4109int i = t.get(Calendar.YEAR);4110int size = 2;4111switch (c) {4112case DateTime.CENTURY -> i /= 100;4113case DateTime.YEAR_2 -> i %= 100;4114case DateTime.YEAR_4 -> size = 4;4115}4116Flags flags = Flags.ZERO_PAD;4117sb.append(localizedMagnitude(null, i, flags, size, l));4118break;4119}4120case DateTime.DAY_OF_MONTH_0: // 'd' (01 - 31)4121case DateTime.DAY_OF_MONTH: { // 'e' (1 - 31) -- like d4122int i = t.get(Calendar.DATE);4123Flags flags = (c == DateTime.DAY_OF_MONTH_04124? Flags.ZERO_PAD4125: Flags.NONE);4126sb.append(localizedMagnitude(null, i, flags, 2, l));4127break;4128}4129case DateTime.DAY_OF_YEAR: { // 'j' (001 - 366)4130int i = t.get(Calendar.DAY_OF_YEAR);4131Flags flags = Flags.ZERO_PAD;4132sb.append(localizedMagnitude(null, i, flags, 3, l));4133break;4134}4135case DateTime.MONTH: { // 'm' (01 - 12)4136int i = t.get(Calendar.MONTH) + 1;4137Flags flags = Flags.ZERO_PAD;4138sb.append(localizedMagnitude(null, i, flags, 2, l));4139break;4140}41414142// Composites4143case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)4144case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)4145char sep = ':';4146print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);4147print(sb, t, DateTime.MINUTE, l);4148if (c == DateTime.TIME) {4149sb.append(sep);4150print(sb, t, DateTime.SECOND, l);4151}4152break;4153}4154case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)4155char sep = ':';4156print(sb, t, DateTime.HOUR_0, l).append(sep);4157print(sb, t, DateTime.MINUTE, l).append(sep);4158print(sb, t, DateTime.SECOND, l).append(' ');4159// this may be in wrong place for some locales4160StringBuilder tsb = new StringBuilder();4161print(tsb, t, DateTime.AM_PM, l);41624163sb.append(toUpperCaseWithLocale(tsb.toString(), l));4164break;4165}4166case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)4167char sep = ' ';4168print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);4169print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);4170print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);4171print(sb, t, DateTime.TIME, l).append(sep);4172print(sb, t, DateTime.ZONE, l).append(sep);4173print(sb, t, DateTime.YEAR_4, l);4174break;4175}4176case DateTime.DATE: { // 'D' (mm/dd/yy)4177char sep = '/';4178print(sb, t, DateTime.MONTH, l).append(sep);4179print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);4180print(sb, t, DateTime.YEAR_2, l);4181break;4182}4183case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)4184char sep = '-';4185print(sb, t, DateTime.YEAR_4, l).append(sep);4186print(sb, t, DateTime.MONTH, l).append(sep);4187print(sb, t, DateTime.DAY_OF_MONTH_0, l);4188break;4189}4190default:4191assert false;4192}4193return sb;4194}41954196private void print(TemporalAccessor t, char c, Locale l) throws IOException {4197StringBuilder sb = new StringBuilder();4198print(sb, t, c, l);4199// justify based on width4200if (f.contains(Flags.UPPERCASE)) {4201appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));4202} else {4203appendJustified(a, sb);4204}4205}42064207private Appendable print(StringBuilder sb, TemporalAccessor t, char c,4208Locale l) throws IOException {4209if (sb == null)4210sb = new StringBuilder();4211try {4212switch (c) {4213case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)4214int i = t.get(ChronoField.HOUR_OF_DAY);4215sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));4216break;4217}4218case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H4219int i = t.get(ChronoField.HOUR_OF_DAY);4220sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));4221break;4222}4223case DateTime.HOUR_0: { // 'I' (01 - 12)4224int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);4225sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));4226break;4227}4228case DateTime.HOUR: { // 'l' (1 - 12) -- like I4229int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);4230sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));4231break;4232}4233case DateTime.MINUTE: { // 'M' (00 - 59)4234int i = t.get(ChronoField.MINUTE_OF_HOUR);4235Flags flags = Flags.ZERO_PAD;4236sb.append(localizedMagnitude(null, i, flags, 2, l));4237break;4238}4239case DateTime.NANOSECOND: { // 'N' (000000000 - 999999999)4240int i;4241try {4242i = t.get(ChronoField.NANO_OF_SECOND);4243} catch (UnsupportedTemporalTypeException u) {4244i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000;4245}4246Flags flags = Flags.ZERO_PAD;4247sb.append(localizedMagnitude(null, i, flags, 9, l));4248break;4249}4250case DateTime.MILLISECOND: { // 'L' (000 - 999)4251int i = t.get(ChronoField.MILLI_OF_SECOND);4252Flags flags = Flags.ZERO_PAD;4253sb.append(localizedMagnitude(null, i, flags, 3, l));4254break;4255}4256case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)4257long i = t.getLong(ChronoField.INSTANT_SECONDS) * 1000L +4258t.getLong(ChronoField.MILLI_OF_SECOND);4259Flags flags = Flags.NONE;4260sb.append(localizedMagnitude(null, i, flags, width, l));4261break;4262}4263case DateTime.AM_PM: { // 'p' (am or pm)4264// Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper4265String[] ampm = { "AM", "PM" };4266if (l != null && l != Locale.US) {4267DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);4268ampm = dfs.getAmPmStrings();4269}4270String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];4271sb.append(s.toLowerCase(Objects.requireNonNullElse(l,4272Locale.getDefault(Locale.Category.FORMAT))));4273break;4274}4275case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)4276long i = t.getLong(ChronoField.INSTANT_SECONDS);4277Flags flags = Flags.NONE;4278sb.append(localizedMagnitude(null, i, flags, width, l));4279break;4280}4281case DateTime.SECOND: { // 'S' (00 - 60 - leap second)4282int i = t.get(ChronoField.SECOND_OF_MINUTE);4283Flags flags = Flags.ZERO_PAD;4284sb.append(localizedMagnitude(null, i, flags, 2, l));4285break;4286}4287case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?4288int i = t.get(ChronoField.OFFSET_SECONDS);4289boolean neg = i < 0;4290sb.append(neg ? '-' : '+');4291if (neg)4292i = -i;4293int min = i / 60;4294// combine minute and hour into a single integer4295int offset = (min / 60) * 100 + (min % 60);4296Flags flags = Flags.ZERO_PAD;4297sb.append(localizedMagnitude(null, offset, flags, 4, l));4298break;4299}4300case DateTime.ZONE: { // 'Z' (symbol)4301ZoneId zid = t.query(TemporalQueries.zone());4302if (zid == null) {4303throw new IllegalFormatConversionException(c, t.getClass());4304}4305if (!(zid instanceof ZoneOffset) &&4306t.isSupported(ChronoField.INSTANT_SECONDS)) {4307Instant instant = Instant.from(t);4308sb.append(TimeZone.getTimeZone(zid.getId())4309.getDisplayName(zid.getRules().isDaylightSavings(instant),4310TimeZone.SHORT,4311Objects.requireNonNullElse(l, Locale.US)));4312break;4313}4314sb.append(zid.getId());4315break;4316}4317// Date4318case DateTime.NAME_OF_DAY_ABBREV: // 'a'4319case DateTime.NAME_OF_DAY: { // 'A'4320int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1;4321Locale lt = Objects.requireNonNullElse(l, Locale.US);4322DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);4323if (c == DateTime.NAME_OF_DAY)4324sb.append(dfs.getWeekdays()[i]);4325else4326sb.append(dfs.getShortWeekdays()[i]);4327break;4328}4329case DateTime.NAME_OF_MONTH_ABBREV: // 'b'4330case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b4331case DateTime.NAME_OF_MONTH: { // 'B'4332int i = t.get(ChronoField.MONTH_OF_YEAR) - 1;4333Locale lt = Objects.requireNonNullElse(l, Locale.US);4334DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);4335if (c == DateTime.NAME_OF_MONTH)4336sb.append(dfs.getMonths()[i]);4337else4338sb.append(dfs.getShortMonths()[i]);4339break;4340}4341case DateTime.CENTURY: // 'C' (00 - 99)4342case DateTime.YEAR_2: // 'y' (00 - 99)4343case DateTime.YEAR_4: { // 'Y' (0000 - 9999)4344int i = t.get(ChronoField.YEAR_OF_ERA);4345int size = 2;4346switch (c) {4347case DateTime.CENTURY -> i /= 100;4348case DateTime.YEAR_2 -> i %= 100;4349case DateTime.YEAR_4 -> size = 4;4350}4351Flags flags = Flags.ZERO_PAD;4352sb.append(localizedMagnitude(null, i, flags, size, l));4353break;4354}4355case DateTime.DAY_OF_MONTH_0: // 'd' (01 - 31)4356case DateTime.DAY_OF_MONTH: { // 'e' (1 - 31) -- like d4357int i = t.get(ChronoField.DAY_OF_MONTH);4358Flags flags = (c == DateTime.DAY_OF_MONTH_04359? Flags.ZERO_PAD4360: Flags.NONE);4361sb.append(localizedMagnitude(null, i, flags, 2, l));4362break;4363}4364case DateTime.DAY_OF_YEAR: { // 'j' (001 - 366)4365int i = t.get(ChronoField.DAY_OF_YEAR);4366Flags flags = Flags.ZERO_PAD;4367sb.append(localizedMagnitude(null, i, flags, 3, l));4368break;4369}4370case DateTime.MONTH: { // 'm' (01 - 12)4371int i = t.get(ChronoField.MONTH_OF_YEAR);4372Flags flags = Flags.ZERO_PAD;4373sb.append(localizedMagnitude(null, i, flags, 2, l));4374break;4375}43764377// Composites4378case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)4379case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)4380char sep = ':';4381print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);4382print(sb, t, DateTime.MINUTE, l);4383if (c == DateTime.TIME) {4384sb.append(sep);4385print(sb, t, DateTime.SECOND, l);4386}4387break;4388}4389case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)4390char sep = ':';4391print(sb, t, DateTime.HOUR_0, l).append(sep);4392print(sb, t, DateTime.MINUTE, l).append(sep);4393print(sb, t, DateTime.SECOND, l).append(' ');4394// this may be in wrong place for some locales4395StringBuilder tsb = new StringBuilder();4396print(tsb, t, DateTime.AM_PM, l);4397sb.append(toUpperCaseWithLocale(tsb.toString(), l));4398break;4399}4400case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)4401char sep = ' ';4402print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);4403print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);4404print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);4405print(sb, t, DateTime.TIME, l).append(sep);4406print(sb, t, DateTime.ZONE, l).append(sep);4407print(sb, t, DateTime.YEAR_4, l);4408break;4409}4410case DateTime.DATE: { // 'D' (mm/dd/yy)4411char sep = '/';4412print(sb, t, DateTime.MONTH, l).append(sep);4413print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);4414print(sb, t, DateTime.YEAR_2, l);4415break;4416}4417case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)4418char sep = '-';4419print(sb, t, DateTime.YEAR_4, l).append(sep);4420print(sb, t, DateTime.MONTH, l).append(sep);4421print(sb, t, DateTime.DAY_OF_MONTH_0, l);4422break;4423}4424default:4425assert false;4426}4427} catch (DateTimeException x) {4428throw new IllegalFormatConversionException(c, t.getClass());4429}4430return sb;4431}44324433// -- Methods to support throwing exceptions --44344435private void failMismatch(Flags f, char c) {4436String fs = f.toString();4437throw new FormatFlagsConversionMismatchException(fs, c);4438}44394440private void failConversion(char c, Object arg) {4441throw new IllegalFormatConversionException(c, arg.getClass());4442}44434444private char getZero(Locale l) {4445if ((l != null) && !l.equals(locale())) {4446DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);4447return dfs.getZeroDigit();4448}4449return zero();4450}44514452private StringBuilder localizedMagnitude(StringBuilder sb,4453long value, Flags f, int width, Locale l) {4454return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);4455}44564457private StringBuilder localizedMagnitude(StringBuilder sb,4458CharSequence value, final int offset, Flags f, int width,4459Locale l) {4460if (sb == null) {4461sb = new StringBuilder();4462}4463int begin = sb.length();44644465char zero = getZero(l);44664467// determine localized grouping separator and size4468char grpSep = '\0';4469int grpSize = -1;4470char decSep = '\0';44714472int len = value.length();4473int dot = len;4474for (int j = offset; j < len; j++) {4475if (value.charAt(j) == '.') {4476dot = j;4477break;4478}4479}44804481if (dot < len) {4482if (l == null || l.equals(Locale.US)) {4483decSep = '.';4484} else {4485DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);4486decSep = dfs.getDecimalSeparator();4487}4488}44894490if (f.contains(Flags.GROUP)) {4491if (l == null || l.equals(Locale.US)) {4492grpSep = ',';4493grpSize = 3;4494} else {4495DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);4496grpSep = dfs.getGroupingSeparator();4497DecimalFormat df = null;4498NumberFormat nf = NumberFormat.getNumberInstance(l);4499if (nf instanceof DecimalFormat) {4500df = (DecimalFormat) nf;4501} else {45024503// Use DecimalFormat constructor to obtain the instance,4504// in case NumberFormat.getNumberInstance(l)4505// returns instance other than DecimalFormat4506LocaleProviderAdapter adapter = LocaleProviderAdapter4507.getAdapter(NumberFormatProvider.class, l);4508if (!(adapter instanceof ResourceBundleBasedAdapter)) {4509adapter = LocaleProviderAdapter.getResourceBundleBased();4510}4511String[] all = adapter.getLocaleResources(l)4512.getNumberPatterns();4513df = new DecimalFormat(all[0], dfs);4514}4515grpSize = df.getGroupingSize();4516// Some locales do not use grouping (the number4517// pattern for these locales does not contain group, e.g.4518// ("#0.###")), but specify a grouping separator.4519// To avoid unnecessary identification of the position of4520// grouping separator, reset its value with null character4521if (!df.isGroupingUsed() || grpSize == 0) {4522grpSep = '\0';4523}4524}4525}45264527// localize the digits inserting group separators as necessary4528for (int j = offset; j < len; j++) {4529if (j == dot) {4530sb.append(decSep);4531// no more group separators after the decimal separator4532grpSep = '\0';4533continue;4534}45354536char c = value.charAt(j);4537sb.append((char) ((c - '0') + zero));4538if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {4539sb.append(grpSep);4540}4541}45424543// apply zero padding4544if (width != -1 && f.contains(Flags.ZERO_PAD)) {4545for (int k = sb.length(); k < width; k++) {4546sb.insert(begin, zero);4547}4548}45494550return sb;4551}45524553// Specialized localization of exponents, where the source value can only4554// contain characters '0' through '9', starting at index offset, and no4555// group separators is added for any locale.4556private void localizedMagnitudeExp(StringBuilder sb, char[] value,4557final int offset, Locale l) {4558char zero = getZero(l);45594560int len = value.length;4561for (int j = offset; j < len; j++) {4562char c = value[j];4563sb.append((char) ((c - '0') + zero));4564}4565}4566}45674568private static class Flags {4569private int flags;45704571static final Flags NONE = new Flags(0); // ''45724573// duplicate declarations from Formattable.java4574static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'4575static final Flags UPPERCASE = new Flags(1<<1); // '^'4576static final Flags ALTERNATE = new Flags(1<<2); // '#'45774578// numerics4579static final Flags PLUS = new Flags(1<<3); // '+'4580static final Flags LEADING_SPACE = new Flags(1<<4); // ' '4581static final Flags ZERO_PAD = new Flags(1<<5); // '0'4582static final Flags GROUP = new Flags(1<<6); // ','4583static final Flags PARENTHESES = new Flags(1<<7); // '('45844585// indexing4586static final Flags PREVIOUS = new Flags(1<<8); // '<'45874588private Flags(int f) {4589flags = f;4590}45914592public int valueOf() {4593return flags;4594}45954596public boolean contains(Flags f) {4597return (flags & f.valueOf()) == f.valueOf();4598}45994600public Flags dup() {4601return new Flags(flags);4602}46034604private Flags add(Flags f) {4605flags |= f.valueOf();4606return this;4607}46084609public Flags remove(Flags f) {4610flags &= ~f.valueOf();4611return this;4612}46134614public static Flags parse(String s, int start, int end) {4615Flags f = new Flags(0);4616for (int i = start; i < end; i++) {4617char c = s.charAt(i);4618Flags v = parse(c);4619if (f.contains(v))4620throw new DuplicateFormatFlagsException(v.toString());4621f.add(v);4622}4623return f;4624}46254626// parse those flags which may be provided by users4627private static Flags parse(char c) {4628return switch (c) {4629case '-' -> LEFT_JUSTIFY;4630case '#' -> ALTERNATE;4631case '+' -> PLUS;4632case ' ' -> LEADING_SPACE;4633case '0' -> ZERO_PAD;4634case ',' -> GROUP;4635case '(' -> PARENTHESES;4636case '<' -> PREVIOUS;4637default -> throw new UnknownFormatFlagsException(String.valueOf(c));4638};4639}46404641// Returns a string representation of the current {@code Flags}.4642public static String toString(Flags f) {4643return f.toString();4644}46454646public String toString() {4647StringBuilder sb = new StringBuilder();4648if (contains(LEFT_JUSTIFY)) sb.append('-');4649if (contains(UPPERCASE)) sb.append('^');4650if (contains(ALTERNATE)) sb.append('#');4651if (contains(PLUS)) sb.append('+');4652if (contains(LEADING_SPACE)) sb.append(' ');4653if (contains(ZERO_PAD)) sb.append('0');4654if (contains(GROUP)) sb.append(',');4655if (contains(PARENTHESES)) sb.append('(');4656if (contains(PREVIOUS)) sb.append('<');4657return sb.toString();4658}4659}46604661private static class Conversion {4662// Byte, Short, Integer, Long, BigInteger4663// (and associated primitives due to autoboxing)4664static final char DECIMAL_INTEGER = 'd';4665static final char OCTAL_INTEGER = 'o';4666static final char HEXADECIMAL_INTEGER = 'x';4667static final char HEXADECIMAL_INTEGER_UPPER = 'X';46684669// Float, Double, BigDecimal4670// (and associated primitives due to autoboxing)4671static final char SCIENTIFIC = 'e';4672static final char SCIENTIFIC_UPPER = 'E';4673static final char GENERAL = 'g';4674static final char GENERAL_UPPER = 'G';4675static final char DECIMAL_FLOAT = 'f';4676static final char HEXADECIMAL_FLOAT = 'a';4677static final char HEXADECIMAL_FLOAT_UPPER = 'A';46784679// Character, Byte, Short, Integer4680// (and associated primitives due to autoboxing)4681static final char CHARACTER = 'c';4682static final char CHARACTER_UPPER = 'C';46834684// java.util.Date, java.util.Calendar, long4685static final char DATE_TIME = 't';4686static final char DATE_TIME_UPPER = 'T';46874688// if (arg.TYPE != boolean) return boolean4689// if (arg != null) return true; else return false;4690static final char BOOLEAN = 'b';4691static final char BOOLEAN_UPPER = 'B';4692// if (arg instanceof Formattable) arg.formatTo()4693// else arg.toString();4694static final char STRING = 's';4695static final char STRING_UPPER = 'S';4696// arg.hashCode()4697static final char HASHCODE = 'h';4698static final char HASHCODE_UPPER = 'H';46994700static final char LINE_SEPARATOR = 'n';4701static final char PERCENT_SIGN = '%';47024703static boolean isValid(char c) {4704return switch (c) {4705case BOOLEAN,4706BOOLEAN_UPPER,4707STRING,4708STRING_UPPER,4709HASHCODE,4710HASHCODE_UPPER,4711CHARACTER,4712CHARACTER_UPPER,4713DECIMAL_INTEGER,4714OCTAL_INTEGER,4715HEXADECIMAL_INTEGER,4716HEXADECIMAL_INTEGER_UPPER,4717SCIENTIFIC,4718SCIENTIFIC_UPPER,4719GENERAL,4720GENERAL_UPPER,4721DECIMAL_FLOAT,4722HEXADECIMAL_FLOAT,4723HEXADECIMAL_FLOAT_UPPER,4724LINE_SEPARATOR,4725PERCENT_SIGN -> true;4726default -> false;4727};4728}47294730// Returns true iff the Conversion is applicable to all objects.4731static boolean isGeneral(char c) {4732return switch (c) {4733case BOOLEAN,4734BOOLEAN_UPPER,4735STRING,4736STRING_UPPER,4737HASHCODE,4738HASHCODE_UPPER -> true;4739default -> false;4740};4741}47424743// Returns true iff the Conversion is applicable to character.4744static boolean isCharacter(char c) {4745return switch (c) {4746case CHARACTER,4747CHARACTER_UPPER -> true;4748default -> false;4749};4750}47514752// Returns true iff the Conversion is an integer type.4753static boolean isInteger(char c) {4754return switch (c) {4755case DECIMAL_INTEGER,4756OCTAL_INTEGER,4757HEXADECIMAL_INTEGER,4758HEXADECIMAL_INTEGER_UPPER -> true;4759default -> false;4760};4761}47624763// Returns true iff the Conversion is a floating-point type.4764static boolean isFloat(char c) {4765return switch (c) {4766case SCIENTIFIC,4767SCIENTIFIC_UPPER,4768GENERAL,4769GENERAL_UPPER,4770DECIMAL_FLOAT,4771HEXADECIMAL_FLOAT,4772HEXADECIMAL_FLOAT_UPPER -> true;4773default -> false;4774};4775}47764777// Returns true iff the Conversion does not require an argument4778static boolean isText(char c) {4779return switch (c) {4780case LINE_SEPARATOR, PERCENT_SIGN -> true;4781default -> false;4782};4783}4784}47854786private static class DateTime {4787static final char HOUR_OF_DAY_0 = 'H'; // (00 - 23)4788static final char HOUR_0 = 'I'; // (01 - 12)4789static final char HOUR_OF_DAY = 'k'; // (0 - 23) -- like H4790static final char HOUR = 'l'; // (1 - 12) -- like I4791static final char MINUTE = 'M'; // (00 - 59)4792static final char NANOSECOND = 'N'; // (000000000 - 999999999)4793static final char MILLISECOND = 'L'; // jdk, not in gnu (000 - 999)4794static final char MILLISECOND_SINCE_EPOCH = 'Q'; // (0 - 99...?)4795static final char AM_PM = 'p'; // (am or pm)4796static final char SECONDS_SINCE_EPOCH = 's'; // (0 - 99...?)4797static final char SECOND = 'S'; // (00 - 60 - leap second)4798static final char TIME = 'T'; // (24 hour hh:mm:ss)4799static final char ZONE_NUMERIC = 'z'; // (-1200 - +1200) - ls minus?4800static final char ZONE = 'Z'; // (symbol)48014802// Date4803static final char NAME_OF_DAY_ABBREV = 'a'; // 'a'4804static final char NAME_OF_DAY = 'A'; // 'A'4805static final char NAME_OF_MONTH_ABBREV = 'b'; // 'b'4806static final char NAME_OF_MONTH = 'B'; // 'B'4807static final char CENTURY = 'C'; // (00 - 99)4808static final char DAY_OF_MONTH_0 = 'd'; // (01 - 31)4809static final char DAY_OF_MONTH = 'e'; // (1 - 31) -- like d4810static final char NAME_OF_MONTH_ABBREV_X = 'h'; // -- same b4811static final char DAY_OF_YEAR = 'j'; // (001 - 366)4812static final char MONTH = 'm'; // (01 - 12)4813static final char YEAR_2 = 'y'; // (00 - 99)4814static final char YEAR_4 = 'Y'; // (0000 - 9999)48154816// Composites4817static final char TIME_12_HOUR = 'r'; // (hh:mm:ss [AP]M)4818static final char TIME_24_HOUR = 'R'; // (hh:mm same as %H:%M)4819static final char DATE_TIME = 'c';4820// (Sat Nov 04 12:02:33 EST 1999)4821static final char DATE = 'D'; // (mm/dd/yy)4822static final char ISO_STANDARD_DATE = 'F'; // (%Y-%m-%d)48234824static boolean isValid(char c) {4825return switch (c) {4826case HOUR_OF_DAY_0, HOUR_0, HOUR_OF_DAY, HOUR, MINUTE, NANOSECOND, MILLISECOND, MILLISECOND_SINCE_EPOCH,4827AM_PM, SECONDS_SINCE_EPOCH, SECOND, TIME, ZONE_NUMERIC, ZONE -> true;4828// Date4829case NAME_OF_DAY_ABBREV, NAME_OF_DAY, NAME_OF_MONTH_ABBREV, NAME_OF_MONTH, CENTURY, DAY_OF_MONTH_0,4830DAY_OF_MONTH, NAME_OF_MONTH_ABBREV_X, DAY_OF_YEAR, MONTH, YEAR_2, YEAR_4 -> true;4831// Composites4832case TIME_12_HOUR, TIME_24_HOUR, DATE_TIME, DATE, ISO_STANDARD_DATE -> true;4833default -> false;4834};4835}4836}4837}483848394840