Home » openjdk-7 » java » util » [javadoc | source]

    1   /* Licensed to the Apache Software Foundation (ASF) under one or more
    2    * contributor license agreements.  See the NOTICE file distributed with
    3    * this work for additional information regarding copyright ownership.
    4    * The ASF licenses this file to You under the Apache License, Version 2.0
    5    * (the "License"); you may not use this file except in compliance with
    6    * the License.  You may obtain a copy of the License at
    7    * 
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    * 
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   package java.util;
   17   
   18   import java.io.BufferedWriter;
   19   import java.io.Closeable;
   20   import java.io.File;
   21   import java.io.FileNotFoundException;
   22   import java.io.FileOutputStream;
   23   import java.io.Flushable;
   24   import java.io.IOException;
   25   import java.io.OutputStream;
   26   import java.io.OutputStreamWriter;
   27   import java.io.PrintStream;
   28   import java.io.UnsupportedEncodingException;
   29   import java.math.BigDecimal;
   30   import java.math.BigInteger;
   31   import java.math.MathContext;
   32   import java.nio.CharBuffer;
   33   import java.nio.charset.Charset;
   34   import java.security.AccessController;
   35   import java.security.PrivilegedAction;
   36   import java.text.DateFormatSymbols;
   37   import java.text.DecimalFormat;
   38   import java.text.DecimalFormatSymbols;
   39   import java.text.NumberFormat;
   40   
   41   /**
   42    * <p>The {@code Formatter} class is a String-formatting utility that is designed
   43    * to work like the {@code printf} function of the C programming language.
   44    * Its key methods are the {@code format} methods which create a formatted
   45    * {@code String} by replacing a set of placeholders (format tokens) with formatted
   46    * values. The style used to format each value is determined by the format
   47    * token used.  For example, the call<br/>
   48    * {@code format("My decimal value is %d and my String is %s.", 3, "Hello");}<br/>
   49    * returns the {@code String}<br/>
   50    * {@code My decimal value is 3 and my String is Hello.}
   51    *
   52    * <p>The format token consists of a percent sign, optionally followed
   53    * by flags and precision arguments, and then a single character that
   54    * indicates the type of value
   55    * being formatted.  If the type is a time/date, then the type character
   56    * {@code t} is followed by an additional character that indicates how the
   57    * date is to be formatted. The two characters {@code <$} immediately
   58    * following the % sign indicate that the previous value should be used again
   59    * instead of moving on to the next value argument. A number {@code n}
   60    * and a dollar sign immediately following the % sign make n the next argument
   61    * to be used.
   62    *
   63    * <p>The available choices are the following:
   64    *
   65    * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
   66    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
   67    * <TD COLSPAN=4>
   68    * <B>Text value types</B></TD>
   69    * </tr>
   70    * <tr>
   71    * <td width="5%">{@code s}</td>
   72    * <td width="10%">String</td>
   73    * <td width="30%">{@code format("%s, %s", "hello", "Hello");}</td>
   74    * <td width="30%">{@code hello, Hello}</td>
   75    * </tr>
   76    * <tr>
   77    * <td width="5%">{@code S}, {@code s}</td>
   78    * <td width="10%">String to capitals</td>
   79    * <td width="30%">{@code format("%S, %S", "hello", "Hello");}</td>
   80    * <td width="30%">{@code HELLO, HELLO}</td>
   81    * </tr>
   82    * <tr>
   83    * <td width="5%">{@code c}</td>
   84    * <td width="10%">Character</td>
   85    * <td width="30%">{@code format("%c, %c", 'd', 0x65);}</td>
   86    * <td width="30%">{@code d, e}</td>
   87    * </tr>
   88    * <tr>
   89    * <td width="5%">{@code C}</td>
   90    * <td width="10%">Character to capitals</td>
   91    * <td width="30%">{@code format("%C, %C", 'd', 0x65);}</td>
   92    * <td width="30%">{@code D, E}</td>
   93    * </tr>
   94    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
   95    * <TD COLSPAN=4>
   96    * <B>Text option flags</B><br/>The value between the
   97    * option and the type character indicates the minimum width in
   98    * characters of the formatted value  </TD>
   99    * </tr>
  100    * <tr>
  101    * <td width="5%">{@code -}</td>
  102    * <td width="10%">Left justify (width value is required)</td>
  103    * <td width="30%">{@code format("%-3C, %3C", 'd', 0x65);}</td>
  104    * <td width="30%">{@code D  ,   E}</td>
  105    * </tr>
  106    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  107    * <TD COLSPAN=4>
  108    * <B>Integer types</B></TD>
  109    * </tr>
  110    * <tr>
  111    * <td width="5%">{@code d}</td>
  112    * <td width="10%">int, formatted as decimal</td>
  113    * <td width="30%">{@code format("%d, %d"1$, 35, 0x10);}</td>
  114    * <td width="30%">{@code 35, 16}</td>
  115    * </tr>
  116    * <tr>
  117    * <td width="5%">{@code o}</td>
  118    * <td width="10%">int, formatted as octal</td>
  119    * <td width="30%">{@code format("%o, %o", 8, 010);}</td>
  120    * <td width="30%">{@code 10, 10}</td>
  121    * </tr>
  122    * <tr>
  123    * <td width="5%">{@code X}, {@code x}</td>
  124    * <td width="10%">int, formatted as hexidecimal</td>
  125    * <td width="30%">{@code format("%x, %X", 10, 10);}</td>
  126    * <td width="30%">{@code a, A}</td>
  127    * </tr>
  128    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  129    * <TD COLSPAN=4>
  130    * <B>Integer option flags</B><br/>The value between the
  131    * option and the type character indicates the minimum width in
  132    * characters of the formatted value  </TD>
  133    * </tr>
  134    * <tr>
  135    * <td width="5%">{@code +}</td>
  136    * <td width="10%">lead with the number's sign</td>
  137    * <td width="30%">{@code format("%+d, %+4d", 5, 5);}</td>
  138    * <td width="30%">{@code +5,   +5}</td>
  139    * </tr>
  140    * <tr>
  141    * <td width="5%">{@code -}</td>
  142    * <td width="10%">Left justify (width value is required)</td>
  143    * <td width="30%">{@code format("%-6dx", 5);}</td>
  144    * <td width="30%">{@code 5      x}</td>
  145    * </tr>
  146    * <tr>
  147    * <td width="5%">{@code #}</td>
  148    * <td width="10%">Print the leading characters that indicate
  149    * hexidecimal or octal (for use only with hex and octal types) </td>
  150    * <td width="30%">{@code format("%#o", 010);}</td>
  151    * <td width="30%">{@code 010}</td>
  152    * </tr>
  153    * <tr>
  154    * <td width="5%">{@code  }</td>
  155    * <td width="10%">A space indicates that non-negative numbers
  156    * should have a leading space. </td>
  157    * <td width="30%">{@code format("x% d% 5d", 4, 4);}</td>
  158    * <td width="30%">{@code x 4    4}</td>
  159    * </tr>
  160    * <tr>
  161    * <td width="5%">{@code 0}</td>
  162    * <td width="10%">Pad the number with leading zeros (width value is required)</td>
  163    * <td width="30%">{@code format("%07d, %03d", 4, 5555);}</td>
  164    * <td width="30%">{@code 0000004, 5555}</td>
  165    * </tr>
  166    * <tr>
  167    * <td width="5%">{@code (}</td>
  168    * <td width="10%">Put parentheses around negative numbers (decimal only)</td>
  169    * <td width="30%">{@code format("%(d, %(d, %(6d", 12, -12, -12);}</td>
  170    * <td width="30%">{@code 12, (12),   (12)}</td>
  171    * </tr>
  172    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  173    * <TD COLSPAN=4>
  174    * <B>Float types</B><br/>A value immediately following the % symbol
  175    * gives the minimum width in characters of the formatted value; if it
  176    * is followed by a period and another integer, then the second value
  177    * gives the precision (6 by default).</TD>
  178    * </tr>
  179    * <tr>
  180    * <td width="5%">{@code f}</td>
  181    * <td width="10%">float (or double) formatted as a decimal, where
  182    * the precision indicates the number of digits after the decimal.</td>
  183    * <td width="30%">{@code format("%f %<.1f %<1.5f %<10f %<6.0f", 123.456f);}</td>
  184    * <td width="30%">{@code 123.456001 123.5 123.45600 123.456001    123}</td>
  185    * </tr>
  186    * <tr>
  187    * <td width="5%">{@code E}, {@code e}</td>
  188    * <td width="10%">float (or double) formatted in decimal exponential
  189    * notation, where the precision indicates the number of significant digits.</td>
  190    * <td width="30%">{@code format("%E %<.1e %<1.5E %<10E %<6.0E", 123.456f);}</td>
  191    * <td width="30%">{@code 1.234560E+02 1.2e+02 1.23456E+02 1.234560E+02  1E+02}</td>
  192    * </tr>
  193    * <tr>
  194    * <td width="5%">{@code G}, {@code g}</td>
  195    * <td width="10%">float (or double) formatted in decimal exponential
  196    * notation , where the precision indicates the maximum number of significant digits.</td>
  197    * <td width="30%">{@code format("%G %<.1g %<1.5G %<10G %<6.0G", 123.456f);}</td>
  198    * <td width="30%">{@code 123.456 1e+02 123.46    123.456  1E+02}</td>
  199    * </tr>
  200    * <tr>
  201    * <td width="5%">{@code A}, {@code a}</td>
  202    * <td width="10%">float (or double) formatted as a hexidecimal in exponential
  203    * notation, where the precision indicates the number of significant digits.</td>
  204    * <td width="30%">{@code format("%A %<.1a %<1.5A %<10A %<6.0A", 123.456f);}</td>
  205    * <td width="30%">{@code 0X1.EDD2F2P6 0x1.fp6 0X1.EDD2FP6 0X1.EDD2F2P6 0X1.FP6}</td>
  206    * </tr>
  207    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  208    * <TD COLSPAN=4>
  209    * <B>Float-type option flags</B><br/>See the Integer-type options.
  210    * The options for float-types are the
  211    * same as for integer types with one addition: </TD>
  212    * </tr>
  213    * <tr>
  214    * <td width="5%">{@code ,}</td>
  215    * <td width="10%">Use a comma in place of a decimal if the locale
  216    * requires it. </td>
  217    * <td width="30%">{@code format(new Locale("fr"), "%,7.2f", 6.03f);}</td>
  218    * <td width="30%">{@code    6,03}</td>
  219    * </tr>
  220    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  221    * <TD COLSPAN=4>
  222    * <B>Date types</B></TD>
  223    * </tr>
  224    * <tr>
  225    * <td width="5%">{@code t}, {@code T}</td>
  226    * <td width="10%">Date</td>
  227    * <td width="30%">{@code format(new Locale("fr"), "%tB %TB", Calendar.getInstance(), Calendar.getInstance());}</td>
  228    * <td width="30%">{@code avril AVRIL}</td>
  229    * </tr>
  230    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  231    * <TD COLSPAN=4>
  232    * <B>Date format precisions</B><br/>The format precision character
  233    * follows the {@code t}. </TD>
  234    * </tr>
  235    * <tr>
  236    * <td width="5%">{@code A}, {@code a}</td>
  237    * <td width="10%">The day of the week</td>
  238    * <td width="30%">{@code format("%ta %tA", cal, cal);}</td>
  239    * <td width="30%">{@code Tue Tuesday}</td>
  240    * </tr>
  241    * <tr>
  242    * <td width="5%">{@code b}, {@code B}, {@code h}</td>
  243    * <td width="10%">The name of the month</td>
  244    * <td width="30%">{@code format("%tb %<tB %<th", cal, cal, cal);}</td>
  245    * <td width="30%">{@code Apr April Apr}</td>
  246    * </tr>
  247    * <tr>
  248    * <td width="5%">{@code C}</td>
  249    * <td width="10%">The century</td>
  250    * <td width="30%">{@code format("%tC\n", cal);}</td>
  251    * <td width="30%">{@code 20}</td>
  252    * </tr>
  253    * <tr>
  254    * <td width="5%">{@code d}, {@code e}</td>
  255    * <td width="10%">The day of the month (with or without leading zeros)</td>
  256    * <td width="30%">{@code format("%td %te", cal, cal);}</td>
  257    * <td width="30%">{@code 01 1}</td>
  258    * </tr>
  259    * <tr>
  260    * <td width="5%">{@code F}</td>
  261    * <td width="10%">The complete date formatted as YYYY-MM-DD</td>
  262    * <td width="30%">{@code format("%tF", cal);}</td>
  263    * <td width="30%">{@code 2008-04-01}</td>
  264    * </tr>
  265    * <tr>
  266    * <td width="5%">{@code D}</td>
  267    * <td width="10%">The complete date formatted as MM/DD/YY
  268    * (not corrected for locale) </td>
  269    * <td width="30%">{@code format(new Locale("en_US"), "%tD", cal);<br/>format(new Locale("en_UK"), " %tD", cal);}</td>
  270    * <td width="30%">{@code 04/01/08 04/01/08}</td>
  271    * </tr>
  272    * <tr>
  273    * <td width="5%">{@code j}</td>
  274    * <td width="10%">The number of the day (from the beginning of the year).</td>
  275    * <td width="30%">{@code format("%tj\n", cal);}</td>
  276    * <td width="30%">{@code 092}</td>
  277    * </tr>
  278    * <tr>
  279    * <td width="5%">{@code m}</td>
  280    * <td width="10%">The number of the month</td>
  281    * <td width="30%">{@code format("%tm\n", cal);}</td>
  282    * <td width="30%">{@code 04}</td>
  283    * </tr>
  284    * <tr>
  285    * <td width="5%">{@code y}, {@code Y}</td>
  286    * <td width="10%">The year</td>
  287    * <td width="30%">{@code format("%ty %tY", cal, cal);}</td>
  288    * <td width="30%">{@code 08 2008}</td>
  289    * </tr>
  290    * <tr>
  291    * <td width="5%">{@code H}, {@code I}, {@code k}, {@code l}</td>
  292    * <td width="10%">The hour of the day, in 12 or 24 hour format, with or
  293    * without a leading zero</td>
  294    * <td width="30%">{@code format("%tH %tI %tk %tl", cal, cal, cal, cal);}</td>
  295    * <td width="30%">{@code 16 04 16 4}</td>
  296    * </tr>
  297    * <tr>
  298    * <td width="5%">{@code p}</td>
  299    * <td width="10%">a.m. or p.m.</td>
  300    * <td width="30%">{@code format("%tp %Tp", cal, cal);}</td>
  301    * <td width="30%">{@code pm PM}</td>
  302    * </tr>
  303    * <tr>
  304    * <td width="5%">{@code M}, {@code S}, {@code L}, {@code N}</td>
  305    * <td width="10%">The minutes, seconds, milliseconds, and nanoseconds</td>
  306    * <td width="30%">{@code format("%tM %tS %tL %tN", cal, cal, cal, cal);}</td>
  307    * <td width="30%">{@code 08 17 359 359000000}</td>
  308    * </tr>
  309    * <tr>
  310    * <td width="5%">{@code Z}, {@code z}</td>
  311    * <td width="10%">The time zone: its abbreviation or offset from GMT</td>
  312    * <td width="30%">{@code format("%tZ %tz", cal, cal);}</td>
  313    * <td width="30%">{@code CEST +0100}</td>
  314    * </tr>
  315    * <tr>
  316    * <td width="5%">{@code R}, {@code r}, {@code T}</td>
  317    * <td width="10%">The complete time</td>
  318    * <td width="30%">{@code format("%tR %tr %tT", cal, cal, cal);}</td>
  319    * <td width="30%">{@code 16:15 04:15:32 PM 16:15:32}</td>
  320    * </tr>
  321    * <tr>
  322    * <td width="5%">{@code s}, {@code Q}</td>
  323    * <td width="10%">The number of seconds or milliseconds from "the epoch"
  324    * (1 January 1970 00:00:00 UTC) </td>
  325    * <td width="30%">{@code format("%ts %tQ", cal, cal);}</td>
  326    * <td width="30%">{@code 1207059412 1207059412656}</td>
  327    * </tr>
  328    * <tr>
  329    * <td width="5%">{@code c}</td>
  330    * <td width="10%">The complete time and date</td>
  331    * <td width="30%">{@code format("%tc", cal);}</td>
  332    * <td width="30%">{@code Tue Apr 01 16:19:17 CEST 2008}</td>
  333    * </tr>
  334    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  335    * <TD COLSPAN=4>
  336    * <B>Other data types</B></TD>
  337    * </tr>
  338    * <tr>
  339    * <td width="5%">{@code B}, {@code b}</td>
  340    * <td width="10%">Boolean</td>
  341    * <td width="30%">{@code format("%b, %B", true, false);}</td>
  342    * <td width="30%">{@code true, FALSE}</td>
  343    * </tr>
  344    * <tr>
  345    * <td width="5%">{@code H}, {@code h}</td>
  346    * <td width="10%">Hashcode</td>
  347    * <td width="30%">{@code format("%h, %H", obj, obj);}</td>
  348    * <td width="30%">{@code 190d11, 190D11}</td>
  349    * </tr>
  350    * <tr>
  351    * <td width="5%">{@code n}</td>
  352    * <td width="10%">line separator</td>
  353    * <td width="30%">{@code format("first%nsecond", "???");}</td>
  354    * <td width="30%">{@code first<br/>second}</td>
  355    * </tr>
  356    * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
  357    * <TD COLSPAN=4>
  358    * <B>Escape sequences</B></TD>
  359    * </tr>
  360    * <tr>
  361    * <td width="5%">{@code %}</td>
  362    * <td width="10%">Escape the % character</td>
  363    * <td width="30%">{@code format("%d%%, %d", 50, 60);}</td>
  364    * <td width="30%">{@code 50%, 60}</td>
  365    * </tr>
  366    * </table>
  367    *
  368    * <p>An instance of Formatter can be created to write the formatted
  369    * output to standard types of output streams.  Its functionality can
  370    * also be accessed through the format methods of an output stream
  371    * or of {@code String}:<br/>
  372    * {@code System.out.println(String.format("%ty\n", cal));}<br/>
  373    * {@code System.out.format("%ty\n", cal);}
  374    *
  375    * <p>The class is not multi-threaded safe. The user is responsible for
  376    * maintaining a thread-safe design if a {@code Formatter} is
  377    * accessed by multiple threads. 
  378    *
  379    * @since 1.5
  380    */
  381   public final class Formatter implements Closeable, Flushable {
  382   
  383       /**
  384        * The enumeration giving the available styles for formatting very large
  385        * decimal numbers.
  386        */
  387       public enum BigDecimalLayoutForm {
  388           /**
  389            * Use scientific style for BigDecimals.
  390            */
  391           SCIENTIFIC,
  392           /**
  393            * Use normal decimal/float style for BigDecimals.
  394            */
  395           DECIMAL_FLOAT
  396       }
  397   
  398       private Appendable out;
  399   
  400       private Locale locale;
  401   
  402       private boolean closed = false;
  403   
  404       private IOException lastIOException;
  405   
  406       /**
  407        * Constructs a {@code Formatter}.
  408        * 
  409        * The output is written to a {@code StringBuilder} which can be acquired by invoking
  410        * {@link #out()} and whose content can be obtained by calling
  411        * {@code toString()}.
  412        * 
  413        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  414        */
  415       public Formatter() {
  416           this(new StringBuilder(), Locale.getDefault());
  417       }
  418   
  419       /**
  420        * Constructs a {@code Formatter} whose output will be written to the
  421        * specified {@code Appendable}.
  422        * 
  423        * The locale for the {@code Formatter} is the default {@code Locale}.
  424        * 
  425        * @param a
  426        *            the output destination of the {@code Formatter}. If {@code a} is {@code null},
  427        *            then a {@code StringBuilder} will be used.
  428        */
  429       public Formatter(Appendable a) {
  430           this(a, Locale.getDefault());
  431       }
  432   
  433       /**
  434        * Constructs a {@code Formatter} with the specified {@code Locale}.
  435        * 
  436        * The output is written to a {@code StringBuilder} which can be acquired by invoking
  437        * {@link #out()} and whose content can be obtained by calling
  438        * {@code toString()}.
  439        * 
  440        * @param l
  441        *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
  442        *            then no localization will be used.
  443        */
  444       public Formatter(Locale l) {
  445           this(new StringBuilder(), l);
  446       }
  447   
  448       /**
  449        * Constructs a {@code Formatter} with the specified {@code Locale}
  450        * and whose output will be written to the
  451        * specified {@code Appendable}.
  452        * 
  453        * @param a
  454        *            the output destination of the {@code Formatter}. If {@code a} is {@code null},
  455        *            then a {@code StringBuilder} will be used.
  456        * @param l
  457        *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
  458        *            then no localization will be used.
  459        */
  460       public Formatter(Appendable a, Locale l) {
  461           if (null == a) {
  462               out = new StringBuilder();
  463           } else {
  464               out = a;
  465           }
  466           locale = l;
  467       }
  468   
  469       /**
  470        * Constructs a {@code Formatter} whose output is written to the specified file.
  471        * 
  472        * The charset of the {@code Formatter} is the default charset.
  473        * 
  474        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  475        * 
  476        * @param fileName
  477        *            the filename of the file that is used as the output
  478        *            destination for the {@code Formatter}. The file will be truncated to
  479        *            zero size if the file exists, or else a new file will be
  480        *            created. The output of the {@code Formatter} is buffered.
  481        * @throws FileNotFoundException
  482        *             if the filename does not denote a normal and writable file,
  483        *             or if a new file cannot be created, or if any error arises when
  484        *             opening or creating the file.
  485        * @throws SecurityException
  486        *             if there is a {@code SecurityManager} in place which denies permission
  487        *             to write to the file in {@code checkWrite(file.getPath())}.
  488        */
  489       public Formatter(String fileName) throws FileNotFoundException {
  490           this(new File(fileName));
  491   
  492       }
  493   
  494       /**
  495        * Constructs a {@code Formatter} whose output is written to the specified file.
  496        * 
  497        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  498        * 
  499        * @param fileName
  500        *            the filename of the file that is used as the output
  501        *            destination for the {@code Formatter}. The file will be truncated to
  502        *            zero size if the file exists, or else a new file will be
  503        *            created. The output of the {@code Formatter} is buffered.
  504        * @param csn
  505        *            the name of the charset for the {@code Formatter}.
  506        * @throws FileNotFoundException
  507        *             if the filename does not denote a normal and writable file,
  508        *             or if a new file cannot be created, or if any error arises when
  509        *             opening or creating the file.
  510        * @throws SecurityException
  511        *             if there is a {@code SecurityManager} in place which denies permission
  512        *             to write to the file in {@code checkWrite(file.getPath())}.
  513        * @throws UnsupportedEncodingException
  514        *             if the charset with the specified name is not supported.
  515        */
  516       public Formatter(String fileName, String csn) throws FileNotFoundException,
  517               UnsupportedEncodingException {
  518           this(new File(fileName), csn);
  519       }
  520   
  521       /**
  522        * Constructs a {@code Formatter} with the given {@code Locale} and charset,
  523        * and whose output is written to the specified file.
  524        * 
  525        * @param fileName
  526        *            the filename of the file that is used as the output
  527        *            destination for the {@code Formatter}. The file will be truncated to
  528        *            zero size if the file exists, or else a new file will be
  529        *            created. The output of the {@code Formatter} is buffered.
  530        * @param csn
  531        *            the name of the charset for the {@code Formatter}.
  532        * @param l
  533        *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
  534        *            then no localization will be used.
  535        * @throws FileNotFoundException
  536        *             if the filename does not denote a normal and writable file,
  537        *             or if a new file cannot be created, or if any error arises when
  538        *             opening or creating the file.
  539        * @throws SecurityException
  540        *             if there is a {@code SecurityManager} in place which denies permission
  541        *             to write to the file in {@code checkWrite(file.getPath())}.
  542        * @throws UnsupportedEncodingException
  543        *             if the charset with the specified name is not supported.
  544        */
  545       public Formatter(String fileName, String csn, Locale l)
  546               throws FileNotFoundException, UnsupportedEncodingException {
  547   
  548           this(new File(fileName), csn, l);
  549       }
  550   
  551       /**
  552        * Constructs a {@code Formatter} whose output is written to the specified {@code File}.
  553        * 
  554        * The charset of the {@code Formatter} is the default charset.
  555        * 
  556        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  557        * 
  558        * @param file
  559        *            the {@code File} that is used as the output destination for the
  560        *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
  561        *            exists, or else a new {@code File} will be created. The output of the
  562        *            {@code Formatter} is buffered.
  563        * @throws FileNotFoundException
  564        *             if the {@code File} is not a normal and writable {@code File}, or if a
  565        *             new {@code File} cannot be created, or if any error rises when opening or
  566        *             creating the {@code File}.
  567        * @throws SecurityException
  568        *             if there is a {@code SecurityManager} in place which denies permission
  569        *             to write to the {@code File} in {@code checkWrite(file.getPath())}.
  570        */
  571       public Formatter(File file) throws FileNotFoundException {
  572           this(new FileOutputStream(file));
  573       }
  574   
  575       /**
  576        * Constructs a {@code Formatter} with the given charset,
  577        * and whose output is written to the specified {@code File}.
  578        * 
  579        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  580        * 
  581        * @param file
  582        *            the {@code File} that is used as the output destination for the
  583        *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
  584        *            exists, or else a new {@code File} will be created. The output of the
  585        *            {@code Formatter} is buffered.
  586        * @param csn
  587        *            the name of the charset for the {@code Formatter}.
  588        * @throws FileNotFoundException
  589        *             if the {@code File} is not a normal and writable {@code File}, or if a
  590        *             new {@code File} cannot be created, or if any error rises when opening or
  591        *             creating the {@code File}.
  592        * @throws SecurityException
  593        *             if there is a {@code SecurityManager} in place which denies permission
  594        *             to write to the {@code File} in {@code checkWrite(file.getPath())}.
  595        * @throws UnsupportedEncodingException
  596        *             if the charset with the specified name is not supported.
  597        */
  598       public Formatter(File file, String csn) throws FileNotFoundException,
  599               UnsupportedEncodingException {
  600           this(file, csn, Locale.getDefault());
  601       }
  602   
  603       /**
  604        * Constructs a {@code Formatter} with the given {@code Locale} and charset,
  605        * and whose output is written to the specified {@code File}.
  606        * 
  607        * @param file
  608        *            the {@code File} that is used as the output destination for the
  609        *            {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File}
  610        *            exists, or else a new {@code File} will be created. The output of the
  611        *            {@code Formatter} is buffered.
  612        * @param csn
  613        *            the name of the charset for the {@code Formatter}.
  614        * @param l
  615        *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
  616        *            then no localization will be used.
  617        * @throws FileNotFoundException
  618        *             if the {@code File} is not a normal and writable {@code File}, or if a
  619        *             new {@code File} cannot be created, or if any error rises when opening or
  620        *             creating the {@code File}.
  621        * @throws SecurityException
  622        *             if there is a {@code SecurityManager} in place which denies permission
  623        *             to write to the {@code File} in {@code checkWrite(file.getPath())}.
  624        * @throws UnsupportedEncodingException
  625        *             if the charset with the specified name is not supported.
  626        */
  627       public Formatter(File file, String csn, Locale l)
  628               throws FileNotFoundException, UnsupportedEncodingException {
  629           FileOutputStream fout = null;
  630           try {
  631               fout = new FileOutputStream(file);
  632               OutputStreamWriter writer = new OutputStreamWriter(fout, csn);
  633               out = new BufferedWriter(writer);
  634           } catch (RuntimeException e) {
  635               closeOutputStream(fout);
  636               throw e;
  637           } catch (UnsupportedEncodingException e) {
  638               closeOutputStream(fout);
  639               throw e;
  640           }
  641   
  642           locale = l;
  643       }
  644   
  645       /**
  646        * Constructs a {@code Formatter} whose output is written to the specified {@code OutputStream}.
  647        * 
  648        * The charset of the {@code Formatter} is the default charset.
  649        * 
  650        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  651        * 
  652        * @param os
  653        *            the stream to be used as the destination of the {@code Formatter}.
  654        */
  655       public Formatter(OutputStream os) {
  656           OutputStreamWriter writer = new OutputStreamWriter(os, Charset
  657                   .defaultCharset());
  658           out = new BufferedWriter(writer);
  659           locale = Locale.getDefault();
  660       }
  661   
  662       /**
  663        * Constructs a {@code Formatter} with the given charset,
  664        * and whose output is written to the specified {@code OutputStream}.
  665        * 
  666        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  667        * 
  668        * @param os
  669        *            the stream to be used as the destination of the {@code Formatter}.
  670        * @param csn
  671        *            the name of the charset for the {@code Formatter}.
  672        * @throws UnsupportedEncodingException
  673        *             if the charset with the specified name is not supported.
  674        */
  675       public Formatter(OutputStream os, String csn)
  676               throws UnsupportedEncodingException {
  677   
  678           this(os, csn, Locale.getDefault());
  679       }
  680   
  681       /**
  682        * Constructs a {@code Formatter} with the given {@code Locale} and charset,
  683        * and whose output is written to the specified {@code OutputStream}.
  684        * 
  685        * @param os
  686        *            the stream to be used as the destination of the {@code Formatter}.
  687        * @param csn
  688        *            the name of the charset for the {@code Formatter}.
  689        * @param l
  690        *            the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null},
  691        *            then no localization will be used.
  692        * @throws UnsupportedEncodingException
  693        *             if the charset with the specified name is not supported.
  694        */
  695       public Formatter(OutputStream os, String csn, Locale l)
  696               throws UnsupportedEncodingException {
  697   
  698           OutputStreamWriter writer = new OutputStreamWriter(os, csn);
  699           out = new BufferedWriter(writer);
  700   
  701           locale = l;
  702       }
  703   
  704       /**
  705        * Constructs a {@code Formatter} whose output is written to the specified {@code PrintStream}.
  706        * 
  707        * The charset of the {@code Formatter} is the default charset.
  708        * 
  709        * The {@code Locale} for the {@code Formatter} is the default {@code Locale}.
  710        * 
  711        * @param ps
  712        *            the {@code PrintStream} used as destination of the {@code Formatter}. If
  713        *            {@code ps} is {@code null}, then a {@code NullPointerException} will
  714        *            be raised.
  715        */
  716       public Formatter(PrintStream ps) {
  717           if (null == ps) {
  718               throw new NullPointerException();
  719           }
  720           out = ps;
  721           locale = Locale.getDefault();
  722       }
  723   
  724       private void checkClosed() {
  725           if (closed) {
  726               throw new FormatterClosedException();
  727           }
  728       }
  729   
  730       /**
  731        * Returns the {@code Locale} of the {@code Formatter}.
  732        * 
  733        * @return the {@code Locale} for the {@code Formatter} or {@code null} for no {@code Locale}.
  734        * @throws FormatterClosedException
  735        *             if the {@code Formatter} has been closed.
  736        */
  737       public Locale locale() {
  738           checkClosed();
  739           return locale;
  740       }
  741   
  742       /**
  743        * Returns the output destination of the {@code Formatter}.
  744        * 
  745        * @return the output destination of the {@code Formatter}.
  746        * @throws FormatterClosedException
  747        *             if the {@code Formatter} has been closed.
  748        */
  749       public Appendable out() {
  750           checkClosed();
  751           return out;
  752       }
  753   
  754       /**
  755        * Returns the content by calling the {@code toString()} method of the output
  756        * destination.
  757        * 
  758        * @return the content by calling the {@code toString()} method of the output
  759        *         destination.
  760        * @throws FormatterClosedException
  761        *             if the {@code Formatter} has been closed.
  762        */
  763       @Override
  764       public String toString() {
  765           checkClosed();
  766           return out.toString();
  767       }
  768   
  769       /**
  770        * Flushes the {@code Formatter}. If the output destination is {@link Flushable},
  771        * then the method {@code flush()} will be called on that destination.
  772        * 
  773        * @throws FormatterClosedException
  774        *             if the {@code Formatter} has been closed.
  775        */
  776       public void flush() {
  777           checkClosed();
  778           if (out instanceof Flushable) {
  779               try {
  780                   ((Flushable) out).flush();
  781               } catch (IOException e) {
  782                   lastIOException = e;
  783               }
  784           }
  785       }
  786   
  787       /**
  788        * Closes the {@code Formatter}. If the output destination is {@link Closeable},
  789        * then the method {@code close()} will be called on that destination.
  790        * 
  791        * If the {@code Formatter} has been closed, then calling the this method will have no
  792        * effect.
  793        * 
  794        * Any method but the {@link #ioException()} that is called after the
  795        * {@code Formatter} has been closed will raise a {@code FormatterClosedException}.
  796        */
  797       public void close() {
  798           closed = true;
  799           try {
  800               if (out instanceof Closeable) {
  801                   ((Closeable) out).close();
  802               }
  803           } catch (IOException e) {
  804   
  805               lastIOException = e;
  806           }
  807       }
  808   
  809       /**
  810        * Returns the last {@code IOException} thrown by the {@code Formatter}'s output
  811        * destination. If the {@code append()} method of the destination does not throw
  812        * {@code IOException}s, the {@code ioException()} method will always return {@code null}.
  813        * 
  814        * @return the last {@code IOException} thrown by the {@code Formatter}'s output
  815        *         destination.
  816        */
  817       public IOException ioException() {
  818           return lastIOException;
  819       }
  820   
  821       /**
  822        * Writes a formatted string to the output destination of the {@code Formatter}.
  823        * 
  824        * @param format
  825        *            a format string.
  826        * @param args
  827        *            the arguments list used in the {@code format()} method. If there are
  828        *            more arguments than those specified by the format string, then
  829        *            the additional arguments are ignored.
  830        * @return this {@code Formatter}.
  831        * @throws IllegalFormatException
  832        *             if the format string is illegal or incompatible with the
  833        *             arguments, or if fewer arguments are sent than those required by
  834        *             the format string, or any other illegal situation.
  835        * @throws FormatterClosedException
  836        *             if the {@code Formatter} has been closed.
  837        */
  838       public Formatter format(String format, Object... args) {
  839           return format(locale, format, args);
  840       }
  841   
  842       /**
  843        * Writes a formatted string to the output destination of the {@code Formatter}.
  844        * 
  845        * @param l
  846        *            the {@code Locale} used in the method. If {@code locale} is
  847        *            {@code null}, then no localization will be applied. This
  848        *            parameter does not influence the {@code Locale} specified during
  849        *            construction.
  850        * @param format
  851        *            a format string.
  852        * @param args
  853        *            the arguments list used in the {@code format()} method. If there are
  854        *            more arguments than those specified by the format string, then
  855        *            the additional arguments are ignored.
  856        * @return this {@code Formatter}.
  857        * @throws IllegalFormatException
  858        *             if the format string is illegal or incompatible with the
  859        *             arguments, or if fewer arguments are sent than those required by
  860        *             the format string, or any other illegal situation.
  861        * @throws FormatterClosedException
  862        *             if the {@code Formatter} has been closed.
  863        */
  864       public Formatter format(Locale l, String format, Object... args) {
  865           checkClosed();
  866           CharBuffer formatBuffer = CharBuffer.wrap(format);
  867           ParserStateMachine parser = new ParserStateMachine(formatBuffer);
  868           Transformer transformer = new Transformer(this, l);
  869   
  870           int currentObjectIndex = 0;
  871           Object lastArgument = null;
  872           boolean hasLastArgumentSet = false;
  873           while (formatBuffer.hasRemaining()) {
  874               parser.reset();
  875               FormatToken token = parser.getNextFormatToken();
  876               String result;
  877               String plainText = token.getPlainText();
  878               if (token.getConversionType() == (char) FormatToken.UNSET) {
  879                   result = plainText;
  880               } else {
  881                   plainText = plainText.substring(0, plainText.indexOf('%'));
  882                   Object argument = null;
  883                   if (token.requireArgument()) {
  884                       int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++
  885                               : token.getArgIndex();
  886                       argument = getArgument(args, index, token, lastArgument,
  887                               hasLastArgumentSet);
  888                       lastArgument = argument;
  889                       hasLastArgumentSet = true;
  890                   }
  891                   result = transformer.transform(token, argument);
  892                   result = (null == result ? plainText : plainText + result);
  893               }
  894               // if output is made by formattable callback
  895               if (null != result) {
  896                   try {
  897                       out.append(result);
  898                   } catch (IOException e) {
  899                       lastIOException = e;
  900                   }
  901               }
  902           }
  903           return this;
  904       }
  905   
  906       private Object getArgument(Object[] args, int index, FormatToken token,
  907               Object lastArgument, boolean hasLastArgumentSet) {
  908           if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) {
  909               throw new MissingFormatArgumentException("<"); //$NON-NLS-1$
  910           }
  911   
  912           if (null == args) {
  913               return null;
  914           }
  915   
  916           if (index >= args.length) {
  917               throw new MissingFormatArgumentException(token.getPlainText());
  918           }
  919   
  920           if (index == FormatToken.LAST_ARGUMENT_INDEX) {
  921               return lastArgument;
  922           }
  923   
  924           return args[index];
  925       }
  926   
  927       private static void closeOutputStream(OutputStream os) {
  928           if (null == os) {
  929               return;
  930           }
  931           try {
  932               os.close();
  933   
  934           } catch (IOException e) {
  935               // silently
  936           }
  937       }
  938   
  939       /*
  940        * Information about the format string of a specified argument, which
  941        * includes the conversion type, flags, width, precision and the argument
  942        * index as well as the plainText that contains the whole format string used
  943        * as the result for output if necessary. Besides, the string for flags is
  944        * recorded to construct corresponding FormatExceptions if necessary.
  945        */
  946       private static class FormatToken {
  947   
  948           static final int LAST_ARGUMENT_INDEX = -2;
  949   
  950           static final int UNSET = -1;
  951   
  952           static final int FLAGS_UNSET = 0;
  953   
  954           static final int DEFAULT_PRECISION = 6;
  955   
  956           static final int FLAG_MINUS = 1;
  957   
  958           static final int FLAG_SHARP = 1 << 1;
  959   
  960           static final int FLAG_ADD = 1 << 2;
  961   
  962           static final int FLAG_SPACE = 1 << 3;
  963   
  964           static final int FLAG_ZERO = 1 << 4;
  965   
  966           static final int FLAG_COMMA = 1 << 5;
  967   
  968           static final int FLAG_PARENTHESIS = 1 << 6;
  969   
  970           private static final int FLAGT_TYPE_COUNT = 6;
  971   
  972           private int formatStringStartIndex;
  973   
  974           private String plainText;
  975   
  976           private int argIndex = UNSET;
  977   
  978           private int flags = 0;
  979   
  980           private int width = UNSET;
  981   
  982           private int precision = UNSET;
  983   
  984           private StringBuilder strFlags = new StringBuilder(FLAGT_TYPE_COUNT);
  985   
  986           private char dateSuffix;// will be used in new feature.
  987   
  988           private char conversionType = (char) UNSET;
  989   
  990           boolean isPrecisionSet() {
  991               return precision != UNSET;
  992           }
  993   
  994           boolean isWidthSet() {
  995               return width != UNSET;
  996           }
  997   
  998           boolean isFlagSet(int flag) {
  999               return 0 != (flags & flag);
 1000           }
 1001   
 1002           int getArgIndex() {
 1003               return argIndex;
 1004           }
 1005   
 1006           void setArgIndex(int index) {
 1007               argIndex = index;
 1008           }
 1009   
 1010           String getPlainText() {
 1011               return plainText;
 1012           }
 1013   
 1014           void setPlainText(String plainText) {
 1015               this.plainText = plainText;
 1016           }
 1017   
 1018           int getWidth() {
 1019               return width;
 1020           }
 1021   
 1022           void setWidth(int width) {
 1023               this.width = width;
 1024           }
 1025   
 1026           int getPrecision() {
 1027               return precision;
 1028           }
 1029   
 1030           void setPrecision(int precise) {
 1031               this.precision = precise;
 1032           }
 1033   
 1034           String getStrFlags() {
 1035               return strFlags.toString();
 1036           }
 1037   
 1038           int getFlags() {
 1039               return flags;
 1040           }
 1041   
 1042           void setFlags(int flags) {
 1043               this.flags = flags;
 1044           }
 1045   
 1046           /*
 1047            * Sets qualified char as one of the flags. If the char is qualified,
 1048            * sets it as a flag and returns true. Or else returns false.
 1049            */
 1050           boolean setFlag(char c) {
 1051               int newFlag;
 1052               switch (c) {
 1053                   case '-': {
 1054                       newFlag = FLAG_MINUS;
 1055                       break;
 1056                   }
 1057                   case '#': {
 1058                       newFlag = FLAG_SHARP;
 1059                       break;
 1060                   }
 1061                   case '+': {
 1062                       newFlag = FLAG_ADD;
 1063                       break;
 1064                   }
 1065                   case ' ': {
 1066                       newFlag = FLAG_SPACE;
 1067                       break;
 1068                   }
 1069                   case '0': {
 1070                       newFlag = FLAG_ZERO;
 1071                       break;
 1072                   }
 1073                   case ',': {
 1074                       newFlag = FLAG_COMMA;
 1075                       break;
 1076                   }
 1077                   case '(': {
 1078                       newFlag = FLAG_PARENTHESIS;
 1079                       break;
 1080                   }
 1081                   default:
 1082                       return false;
 1083               }
 1084               if (0 != (flags & newFlag)) {
 1085                   throw new DuplicateFormatFlagsException(String.valueOf(c));
 1086               }
 1087               flags = (flags | newFlag);
 1088               strFlags.append(c);
 1089               return true;
 1090   
 1091           }
 1092   
 1093           int getFormatStringStartIndex() {
 1094               return formatStringStartIndex;
 1095           }
 1096   
 1097           void setFormatStringStartIndex(int index) {
 1098               formatStringStartIndex = index;
 1099           }
 1100   
 1101           char getConversionType() {
 1102               return conversionType;
 1103           }
 1104   
 1105           void setConversionType(char c) {
 1106               conversionType = c;
 1107           }
 1108   
 1109           char getDateSuffix() {
 1110               return dateSuffix;
 1111           }
 1112   
 1113           void setDateSuffix(char c) {
 1114               dateSuffix = c;
 1115           }
 1116   
 1117           boolean requireArgument() {
 1118               return conversionType != '%' && conversionType != 'n';
 1119           }
 1120       }
 1121   
 1122       /*
 1123        * Transforms the argument to the formatted string according to the format
 1124        * information contained in the format token.
 1125        */
 1126       private static class Transformer {
 1127   
 1128           private Formatter formatter;
 1129   
 1130           private FormatToken formatToken;
 1131   
 1132           private Object arg;
 1133   
 1134           private Locale locale;
 1135   
 1136           private static String lineSeparator;
 1137   
 1138           private NumberFormat numberFormat;
 1139   
 1140           private DecimalFormatSymbols decimalFormatSymbols;
 1141   
 1142           private DateTimeUtil dateTimeUtil;
 1143   
 1144           Transformer(Formatter formatter, Locale locale) {
 1145               this.formatter = formatter;
 1146               this.locale = (null == locale ? Locale.US : locale);
 1147           }
 1148   
 1149           private NumberFormat getNumberFormat() {
 1150               if (null == numberFormat) {
 1151                   numberFormat = NumberFormat.getInstance(locale);
 1152               }
 1153               return numberFormat;
 1154           }
 1155   
 1156           private DecimalFormatSymbols getDecimalFormatSymbols() {
 1157               if (null == decimalFormatSymbols) {
 1158                   decimalFormatSymbols = new DecimalFormatSymbols(locale);
 1159               }
 1160               return decimalFormatSymbols;
 1161           }
 1162   
 1163           /*
 1164            * Gets the formatted string according to the format token and the
 1165            * argument.
 1166            */
 1167           String transform(FormatToken token, Object argument) {
 1168   
 1169               /* init data member to print */
 1170               this.formatToken = token;
 1171               this.arg = argument;
 1172   
 1173               String result;
 1174               switch (token.getConversionType()) {
 1175                   case 'B':
 1176                   case 'b': {
 1177                       result = transformFromBoolean();
 1178                       break;
 1179                   }
 1180                   case 'H':
 1181                   case 'h': {
 1182                       result = transformFromHashCode();
 1183                       break;
 1184                   }
 1185                   case 'S':
 1186                   case 's': {
 1187                       result = transformFromString();
 1188                       break;
 1189                   }
 1190                   case 'C':
 1191                   case 'c': {
 1192                       result = transformFromCharacter();
 1193                       break;
 1194                   }
 1195                   case 'd':
 1196                   case 'o':
 1197                   case 'x':
 1198                   case 'X': {
 1199                       if (null == arg || arg instanceof BigInteger) {
 1200                           result = transformFromBigInteger();
 1201                       } else {
 1202                           result = transformFromInteger();
 1203                       }
 1204                       break;
 1205                   }
 1206                   case 'e':
 1207                   case 'E':
 1208                   case 'g':
 1209                   case 'G':
 1210                   case 'f':
 1211                   case 'a':
 1212                   case 'A': {
 1213                       result = transformFromFloat();
 1214                       break;
 1215                   }
 1216                   case '%': {
 1217                       result = transformFromPercent();
 1218                       break;
 1219                   }
 1220                   case 'n': {
 1221                       result = transformFromLineSeparator();
 1222                       break;
 1223                   }
 1224                   case 't':
 1225                   case 'T': {
 1226                       result = transformFromDateTime();
 1227                       break;
 1228                   }
 1229                   default: {
 1230                       throw new UnknownFormatConversionException(String
 1231                               .valueOf(token.getConversionType()));
 1232                   }
 1233               }
 1234   
 1235               if (Character.isUpperCase(token.getConversionType())) {
 1236                   if (null != result) {
 1237                       result = result.toUpperCase(Locale.US);
 1238                   }
 1239               }
 1240               return result;
 1241           }
 1242   
 1243           /*
 1244            * Transforms the Boolean argument to a formatted string.
 1245            */
 1246           private String transformFromBoolean() {
 1247               StringBuilder result = new StringBuilder();
 1248               int startIndex = 0;
 1249               int flags = formatToken.getFlags();
 1250   
 1251               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1252                       && !formatToken.isWidthSet()) {
 1253                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1254                           + formatToken.getConversionType());
 1255               }
 1256   
 1257               // only '-' is valid for flags
 1258               if (FormatToken.FLAGS_UNSET != flags
 1259                       && FormatToken.FLAG_MINUS != flags) {
 1260                   throw new FormatFlagsConversionMismatchException(formatToken
 1261                           .getStrFlags(), formatToken.getConversionType());
 1262               }
 1263   
 1264               if (null == arg) {
 1265                   result.append("false"); //$NON-NLS-1$
 1266               } else if (arg instanceof Boolean) {
 1267                   result.append(arg);
 1268               } else {
 1269                   result.append("true"); //$NON-NLS-1$
 1270               }
 1271               return padding(result, startIndex);
 1272           }
 1273   
 1274           /*
 1275            * Transforms the hashcode of the argument to a formatted string.
 1276            */
 1277           private String transformFromHashCode() {
 1278               StringBuilder result = new StringBuilder();
 1279   
 1280               int startIndex = 0;
 1281               int flags = formatToken.getFlags();
 1282   
 1283               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1284                       && !formatToken.isWidthSet()) {
 1285                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1286                           + formatToken.getConversionType());
 1287               }
 1288   
 1289               // only '-' is valid for flags
 1290               if (FormatToken.FLAGS_UNSET != flags
 1291                       && FormatToken.FLAG_MINUS != flags) {
 1292                   throw new FormatFlagsConversionMismatchException(formatToken
 1293                           .getStrFlags(), formatToken.getConversionType());
 1294               }
 1295   
 1296               if (null == arg) {
 1297                   result.append("null"); //$NON-NLS-1$
 1298               } else {
 1299                   result.append(Integer.toHexString(arg.hashCode()));
 1300               }
 1301               return padding(result, startIndex);
 1302           }
 1303   
 1304           /*
 1305            * Transforms the String to a formatted string.
 1306            */
 1307           private String transformFromString() {
 1308               StringBuilder result = new StringBuilder();
 1309               int startIndex = 0;
 1310               int flags = formatToken.getFlags();
 1311   
 1312               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1313                       && !formatToken.isWidthSet()) {
 1314                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1315                           + formatToken.getConversionType());
 1316               }
 1317   
 1318               if (arg instanceof Formattable) {
 1319                   int flag = 0;
 1320                   // only minus and sharp flag is valid
 1321                   if (FormatToken.FLAGS_UNSET != (flags & ~FormatToken.FLAG_MINUS & ~FormatToken.FLAG_SHARP)) {
 1322                       throw new IllegalFormatFlagsException(formatToken
 1323                               .getStrFlags());
 1324                   }
 1325                   if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)) {
 1326                       flag |= FormattableFlags.LEFT_JUSTIFY;
 1327                   }
 1328                   if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
 1329                       flag |= FormattableFlags.ALTERNATE;
 1330                   }
 1331                   if (Character.isUpperCase(formatToken.getConversionType())) {
 1332                       flag |= FormattableFlags.UPPERCASE;
 1333                   }
 1334                   ((Formattable) arg).formatTo(formatter, flag, formatToken
 1335                           .getWidth(), formatToken.getPrecision());
 1336                   // all actions have been taken out in the
 1337                   // Formattable.formatTo, thus there is nothing to do, just
 1338                   // returns null, which tells the Parser to add nothing to the
 1339                   // output.
 1340                   return null;
 1341               }
 1342               // only '-' is valid for flags if the argument is not an
 1343               // instance of Formattable
 1344               if (FormatToken.FLAGS_UNSET != flags
 1345                       && FormatToken.FLAG_MINUS != flags) {
 1346                   throw new FormatFlagsConversionMismatchException(formatToken
 1347                           .getStrFlags(), formatToken.getConversionType());
 1348               }
 1349   
 1350               result.append(arg);
 1351               return padding(result, startIndex);
 1352           }
 1353   
 1354           /*
 1355            * Transforms the Character to a formatted string.
 1356            */
 1357           private String transformFromCharacter() {
 1358               StringBuilder result = new StringBuilder();
 1359   
 1360               int startIndex = 0;
 1361               int flags = formatToken.getFlags();
 1362   
 1363               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1364                       && !formatToken.isWidthSet()) {
 1365                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1366                           + formatToken.getConversionType());
 1367               }
 1368   
 1369               // only '-' is valid for flags
 1370               if (FormatToken.FLAGS_UNSET != flags
 1371                       && FormatToken.FLAG_MINUS != flags) {
 1372                   throw new FormatFlagsConversionMismatchException(formatToken
 1373                           .getStrFlags(), formatToken.getConversionType());
 1374               }
 1375   
 1376               if (formatToken.isPrecisionSet()) {
 1377                   throw new IllegalFormatPrecisionException(formatToken
 1378                           .getPrecision());
 1379               }
 1380   
 1381               if (null == arg) {
 1382                   result.append("null"); //$NON-NLS-1$
 1383               } else {
 1384                   if (arg instanceof Character) {
 1385                       result.append(arg);
 1386                   } else if (arg instanceof Byte) {
 1387                       byte b = ((Byte) arg).byteValue();
 1388                       if (!Character.isValidCodePoint(b)) {
 1389                           throw new IllegalFormatCodePointException(b);
 1390                       }
 1391                       result.append((char) b);
 1392                   } else if (arg instanceof Short) {
 1393                       short s = ((Short) arg).shortValue();
 1394                       if (!Character.isValidCodePoint(s)) {
 1395                           throw new IllegalFormatCodePointException(s);
 1396                       }
 1397                       result.append((char) s);
 1398                   } else if (arg instanceof Integer) {
 1399                       int codePoint = ((Integer) arg).intValue();
 1400                       if (!Character.isValidCodePoint(codePoint)) {
 1401                           throw new IllegalFormatCodePointException(codePoint);
 1402                       }
 1403                       result.append(String.valueOf(Character.toChars(codePoint)));
 1404                   } else {
 1405                       // argument of other class is not acceptable.
 1406                       throw new IllegalFormatConversionException(formatToken
 1407                               .getConversionType(), arg.getClass());
 1408                   }
 1409               }
 1410               return padding(result, startIndex);
 1411           }
 1412   
 1413           /*
 1414            * Transforms percent to a formatted string. Only '-' is legal flag.
 1415            * Precision is illegal.
 1416            */
 1417           private String transformFromPercent() {
 1418               StringBuilder result = new StringBuilder("%"); //$NON-NLS-1$
 1419   
 1420               int startIndex = 0;
 1421               int flags = formatToken.getFlags();
 1422   
 1423               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1424                       && !formatToken.isWidthSet()) {
 1425                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1426                           + formatToken.getConversionType());
 1427               }
 1428   
 1429               if (FormatToken.FLAGS_UNSET != flags
 1430                       && FormatToken.FLAG_MINUS != flags) {
 1431                   throw new FormatFlagsConversionMismatchException(formatToken
 1432                           .getStrFlags(), formatToken.getConversionType());
 1433               }
 1434               if (formatToken.isPrecisionSet()) {
 1435                   throw new IllegalFormatPrecisionException(formatToken
 1436                           .getPrecision());
 1437               }
 1438               return padding(result, startIndex);
 1439           }
 1440   
 1441           /*
 1442            * Transforms line separator to a formatted string. Any flag, the width
 1443            * or the precision is illegal.
 1444            */
 1445           private String transformFromLineSeparator() {
 1446               if (formatToken.isPrecisionSet()) {
 1447                   throw new IllegalFormatPrecisionException(formatToken
 1448                           .getPrecision());
 1449               }
 1450   
 1451               if (formatToken.isWidthSet()) {
 1452                   throw new IllegalFormatWidthException(formatToken.getWidth());
 1453               }
 1454   
 1455               int flags = formatToken.getFlags();
 1456               if (FormatToken.FLAGS_UNSET != flags) {
 1457                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1458               }
 1459   
 1460               if (null == lineSeparator) {
 1461                   lineSeparator = AccessController
 1462                           .doPrivileged(new PrivilegedAction<String>() {
 1463   
 1464                               public String run() {
 1465                                   return System.getProperty("line.separator"); //$NON-NLS-1$
 1466                               }
 1467                           });
 1468               }
 1469               return lineSeparator;
 1470           }
 1471   
 1472           /*
 1473            * Pads characters to the formatted string.
 1474            */
 1475           private String padding(StringBuilder source, int startIndex) {
 1476               int start = startIndex;
 1477               boolean paddingRight = formatToken
 1478                       .isFlagSet(FormatToken.FLAG_MINUS);
 1479               char paddingChar = '\u0020';// space as padding char.
 1480               if (formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1481                   if ('d' == formatToken.getConversionType()) {
 1482                       paddingChar = getDecimalFormatSymbols().getZeroDigit();
 1483                   } else {
 1484                       paddingChar = '0';
 1485                   }
 1486               } else {
 1487                   // if padding char is space, always padding from the head
 1488                   // location.
 1489                   start = 0;
 1490               }
 1491               int width = formatToken.getWidth();
 1492               int precision = formatToken.getPrecision();
 1493   
 1494               int length = source.length();
 1495               if (precision >= 0) {
 1496                   length = Math.min(length, precision);
 1497                   source.delete(length, source.length());
 1498               }
 1499               if (width > 0) {
 1500                   width = Math.max(source.length(), width);
 1501               }
 1502               if (length >= width) {
 1503                   return source.toString();
 1504               }
 1505   
 1506               char[] paddings = new char[width - length];
 1507               Arrays.fill(paddings, paddingChar);
 1508               String insertString = new String(paddings);
 1509   
 1510               if (paddingRight) {
 1511                   source.append(insertString);
 1512               } else {
 1513                   source.insert(start, insertString);
 1514               }
 1515               return source.toString();
 1516           }
 1517   
 1518           /*
 1519            * Transforms the Integer to a formatted string.
 1520            */
 1521           private String transformFromInteger() {
 1522               int startIndex = 0;
 1523               boolean isNegative = false;
 1524               StringBuilder result = new StringBuilder();
 1525               char currentConversionType = formatToken.getConversionType();
 1526               long value;
 1527   
 1528               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1529                       || formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1530                   if (!formatToken.isWidthSet()) {
 1531                       throw new MissingFormatWidthException(formatToken
 1532                               .getStrFlags());
 1533                   }
 1534               }
 1535               // Combination of '+' & ' ' is illegal.
 1536               if (formatToken.isFlagSet(FormatToken.FLAG_ADD)
 1537                       && formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1538                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1539               }
 1540               if (formatToken.isPrecisionSet()) {
 1541                   throw new IllegalFormatPrecisionException(formatToken
 1542                           .getPrecision());
 1543               }
 1544               if (arg instanceof Long) {
 1545                   value = ((Long) arg).longValue();
 1546               } else if (arg instanceof Integer) {
 1547                   value = ((Integer) arg).longValue();
 1548               } else if (arg instanceof Short) {
 1549                   value = ((Short) arg).longValue();
 1550               } else if (arg instanceof Byte) {
 1551                   value = ((Byte) arg).longValue();
 1552               } else {
 1553                   throw new IllegalFormatConversionException(formatToken
 1554                           .getConversionType(), arg.getClass());
 1555               }
 1556               if ('d' != currentConversionType) {
 1557                   if (formatToken.isFlagSet(FormatToken.FLAG_ADD)
 1558                           || formatToken.isFlagSet(FormatToken.FLAG_SPACE)
 1559                           || formatToken.isFlagSet(FormatToken.FLAG_COMMA)
 1560                           || formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1561                       throw new FormatFlagsConversionMismatchException(
 1562                               formatToken.getStrFlags(), formatToken
 1563                                       .getConversionType());
 1564                   }
 1565               }
 1566   
 1567               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
 1568                   if ('d' == currentConversionType) {
 1569                       throw new FormatFlagsConversionMismatchException(
 1570                               formatToken.getStrFlags(), formatToken
 1571                                       .getConversionType());
 1572                   } else if ('o' == currentConversionType) {
 1573                       result.append("0"); //$NON-NLS-1$
 1574                       startIndex += 1;
 1575                   } else {
 1576                       result.append("0x"); //$NON-NLS-1$
 1577                       startIndex += 2;
 1578                   }
 1579               }
 1580   
 1581               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1582                       && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1583                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1584               }
 1585   
 1586               if (value < 0) {
 1587                   isNegative = true;
 1588               }
 1589   
 1590               if ('d' == currentConversionType) {
 1591                   NumberFormat numberFormat = getNumberFormat();
 1592                   if (formatToken.isFlagSet(FormatToken.FLAG_COMMA)) {
 1593                       numberFormat.setGroupingUsed(true);
 1594                   } else {
 1595                       numberFormat.setGroupingUsed(false);
 1596                   }
 1597                   result.append(numberFormat.format(arg));
 1598               } else {
 1599                   long BYTE_MASK = 0x00000000000000FFL;
 1600                   long SHORT_MASK = 0x000000000000FFFFL;
 1601                   long INT_MASK = 0x00000000FFFFFFFFL;
 1602                   if (isNegative) {
 1603                       if (arg instanceof Byte) {
 1604                           value &= BYTE_MASK;
 1605                       } else if (arg instanceof Short) {
 1606                           value &= SHORT_MASK;
 1607                       } else if (arg instanceof Integer) {
 1608                           value &= INT_MASK;
 1609                       }
 1610                   }
 1611                   if ('o' == currentConversionType) {
 1612                       result.append(Long.toOctalString(value));
 1613                   } else {
 1614                       result.append(Long.toHexString(value));
 1615                   }
 1616                   isNegative = false;
 1617               }
 1618   
 1619               if (!isNegative) {
 1620                   if (formatToken.isFlagSet(FormatToken.FLAG_ADD)) {
 1621                       result.insert(0, '+');
 1622                       startIndex += 1;
 1623                   }
 1624                   if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1625                       result.insert(0, ' ');
 1626                       startIndex += 1;
 1627                   }
 1628               }
 1629   
 1630               /* pad paddingChar to the output */
 1631               if (isNegative
 1632                       && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1633                   result = wrapParentheses(result);
 1634                   return result.toString();
 1635   
 1636               }
 1637               if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1638                   startIndex++;
 1639               }
 1640               return padding(result, startIndex);
 1641           }
 1642   
 1643           /*
 1644            * add () to the output,if the value is negative and
 1645            * formatToken.FLAG_PARENTHESIS is set. 'result' is used as an in-out
 1646            * parameter.
 1647            */
 1648           private StringBuilder wrapParentheses(StringBuilder result) {
 1649               // delete the '-'
 1650               result.deleteCharAt(0);
 1651               result.insert(0, '(');
 1652               if (formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1653                   formatToken.setWidth(formatToken.getWidth() - 1);
 1654                   padding(result, 1);
 1655                   result.append(')');
 1656               } else {
 1657                   result.append(')');
 1658                   padding(result, 0);
 1659               }
 1660               return result;
 1661           }
 1662   
 1663           private String transformFromSpecialNumber() {
 1664               String source = null;
 1665   
 1666               if (!(arg instanceof Number) || arg instanceof BigDecimal) {
 1667                   return null;
 1668               }
 1669   
 1670               Number number = (Number) arg;
 1671               double d = number.doubleValue();
 1672               if (Double.isNaN(d)) {
 1673                   source = "NaN"; //$NON-NLS-1$
 1674               } else if (Double.isInfinite(d)) {
 1675                   if (d >= 0) {
 1676                       if (formatToken.isFlagSet(FormatToken.FLAG_ADD)) {
 1677                           source = "+Infinity"; //$NON-NLS-1$
 1678                       } else if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1679                           source = " Infinity"; //$NON-NLS-1$
 1680                       } else {
 1681                           source = "Infinity"; //$NON-NLS-1$
 1682                       }
 1683                   } else {
 1684                       if (formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1685                           source = "(Infinity)"; //$NON-NLS-1$
 1686                       } else {
 1687                           source = "-Infinity"; //$NON-NLS-1$
 1688                       }
 1689                   }
 1690               }
 1691   
 1692               if (null != source) {
 1693                   formatToken.setPrecision(FormatToken.UNSET);
 1694                   formatToken.setFlags(formatToken.getFlags()
 1695                           & (~FormatToken.FLAG_ZERO));
 1696                   source = padding(new StringBuilder(source), 0);
 1697               }
 1698               return source;
 1699           }
 1700   
 1701           private String transformFromNull() {
 1702               formatToken.setFlags(formatToken.getFlags()
 1703                       & (~FormatToken.FLAG_ZERO));
 1704               return padding(new StringBuilder("null"), 0); //$NON-NLS-1$
 1705           }
 1706   
 1707           /*
 1708            * Transforms a BigInteger to a formatted string.
 1709            */
 1710           private String transformFromBigInteger() {
 1711               int startIndex = 0;
 1712               boolean isNegative = false;
 1713               StringBuilder result = new StringBuilder();
 1714               BigInteger bigInt = (BigInteger) arg;
 1715               char currentConversionType = formatToken.getConversionType();
 1716   
 1717               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1718                       || formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1719                   if (!formatToken.isWidthSet()) {
 1720                       throw new MissingFormatWidthException(formatToken
 1721                               .getStrFlags());
 1722                   }
 1723               }
 1724   
 1725               // Combination of '+' & ' ' is illegal.
 1726               if (formatToken.isFlagSet(FormatToken.FLAG_ADD)
 1727                       && formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1728                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1729               }
 1730   
 1731               // Combination of '-' & '0' is illegal.
 1732               if (formatToken.isFlagSet(FormatToken.FLAG_ZERO)
 1733                       && formatToken.isFlagSet(FormatToken.FLAG_MINUS)) {
 1734                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1735               }
 1736   
 1737               if (formatToken.isPrecisionSet()) {
 1738                   throw new IllegalFormatPrecisionException(formatToken
 1739                           .getPrecision());
 1740               }
 1741   
 1742               if ('d' != currentConversionType
 1743                       && formatToken.isFlagSet(FormatToken.FLAG_COMMA)) {
 1744                   throw new FormatFlagsConversionMismatchException(formatToken
 1745                           .getStrFlags(), currentConversionType);
 1746               }
 1747   
 1748               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)
 1749                       && 'd' == currentConversionType) {
 1750                   throw new FormatFlagsConversionMismatchException(formatToken
 1751                           .getStrFlags(), currentConversionType);
 1752               }
 1753   
 1754               if (null == bigInt) {
 1755                   return transformFromNull();
 1756               }
 1757   
 1758               isNegative = (bigInt.compareTo(BigInteger.ZERO) < 0);
 1759   
 1760               if ('d' == currentConversionType) {
 1761                   NumberFormat numberFormat = getNumberFormat();
 1762                   boolean readableName = formatToken
 1763                           .isFlagSet(FormatToken.FLAG_COMMA);
 1764                   numberFormat.setGroupingUsed(readableName);
 1765                   result.append(numberFormat.format(bigInt));
 1766               } else if ('o' == currentConversionType) {
 1767                   // convert BigInteger to a string presentation using radix 8
 1768                   result.append(bigInt.toString(8));
 1769               } else {
 1770                   // convert BigInteger to a string presentation using radix 16
 1771                   result.append(bigInt.toString(16));
 1772               }
 1773               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
 1774                   startIndex = isNegative ? 1 : 0;
 1775                   if ('o' == currentConversionType) {
 1776                       result.insert(startIndex, "0"); //$NON-NLS-1$
 1777                       startIndex += 1;
 1778                   } else if ('x' == currentConversionType
 1779                           || 'X' == currentConversionType) {
 1780                       result.insert(startIndex, "0x"); //$NON-NLS-1$
 1781                       startIndex += 2;
 1782                   }
 1783               }
 1784   
 1785               if (!isNegative) {
 1786                   if (formatToken.isFlagSet(FormatToken.FLAG_ADD)) {
 1787                       result.insert(0, '+');
 1788                       startIndex += 1;
 1789                   }
 1790                   if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1791                       result.insert(0, ' ');
 1792                       startIndex += 1;
 1793                   }
 1794               }
 1795   
 1796               /* pad paddingChar to the output */
 1797               if (isNegative
 1798                       && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1799                   result = wrapParentheses(result);
 1800                   return result.toString();
 1801   
 1802               }
 1803               if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1804                   startIndex++;
 1805               }
 1806               return padding(result, startIndex);
 1807           }
 1808   
 1809           /*
 1810            * Transforms a Float,Double or BigDecimal to a formatted string.
 1811            */
 1812           private String transformFromFloat() {
 1813               StringBuilder result = new StringBuilder();
 1814               int startIndex = 0;
 1815               char currentConversionType = formatToken.getConversionType();
 1816   
 1817               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS
 1818                       | FormatToken.FLAG_ZERO)) {
 1819                   if (!formatToken.isWidthSet()) {
 1820                       throw new MissingFormatWidthException(formatToken
 1821                               .getStrFlags());
 1822                   }
 1823               }
 1824   
 1825               if (formatToken.isFlagSet(FormatToken.FLAG_ADD)
 1826                       && formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1827                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1828               }
 1829   
 1830               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1831                       && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
 1832                   throw new IllegalFormatFlagsException(formatToken.getStrFlags());
 1833               }
 1834   
 1835               if ('e' == Character.toLowerCase(currentConversionType)) {
 1836                   if (formatToken.isFlagSet(FormatToken.FLAG_COMMA)) {
 1837                       throw new FormatFlagsConversionMismatchException(
 1838                               formatToken.getStrFlags(), currentConversionType);
 1839                   }
 1840               }
 1841   
 1842               if ('g' == Character.toLowerCase(currentConversionType)) {
 1843                   if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
 1844                       throw new FormatFlagsConversionMismatchException(
 1845                               formatToken.getStrFlags(), currentConversionType);
 1846                   }
 1847               }
 1848   
 1849               if ('a' == Character.toLowerCase(currentConversionType)) {
 1850                   if (formatToken.isFlagSet(FormatToken.FLAG_COMMA)
 1851                           || formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1852                       throw new FormatFlagsConversionMismatchException(
 1853                               formatToken.getStrFlags(), currentConversionType);
 1854                   }
 1855               }
 1856   
 1857               if (null == arg) {
 1858                   return transformFromNull();
 1859               }
 1860   
 1861               if (!(arg instanceof Float || arg instanceof Double || arg instanceof BigDecimal)) {
 1862                   throw new IllegalFormatConversionException(
 1863                           currentConversionType, arg.getClass());
 1864               }
 1865   
 1866               String specialNumberResult = transformFromSpecialNumber();
 1867               if (null != specialNumberResult) {
 1868                   return specialNumberResult;
 1869               }
 1870   
 1871               if ('a' != Character.toLowerCase(currentConversionType)) {
 1872                   formatToken
 1873                           .setPrecision(formatToken.isPrecisionSet() ? formatToken
 1874                                   .getPrecision()
 1875                                   : FormatToken.DEFAULT_PRECISION);
 1876               }
 1877               // output result
 1878               FloatUtil floatUtil = new FloatUtil(result, formatToken,
 1879                       (DecimalFormat) NumberFormat.getInstance(locale), arg);
 1880               floatUtil.transform(formatToken, result);
 1881   
 1882               formatToken.setPrecision(FormatToken.UNSET);
 1883   
 1884               if (getDecimalFormatSymbols().getMinusSign() == result.charAt(0)) {
 1885                   if (formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
 1886                       result = wrapParentheses(result);
 1887                       return result.toString();
 1888                   }
 1889               } else {
 1890                   if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
 1891                       result.insert(0, ' ');
 1892                       startIndex++;
 1893                   }
 1894                   if (formatToken.isFlagSet(FormatToken.FLAG_ADD)) {
 1895                       result.insert(0, floatUtil.getAddSign());
 1896                       startIndex++;
 1897                   }
 1898               }
 1899   
 1900               char firstChar = result.charAt(0);
 1901               if (formatToken.isFlagSet(FormatToken.FLAG_ZERO)
 1902                       && (firstChar == floatUtil.getAddSign() || firstChar == floatUtil
 1903                               .getMinusSign())) {
 1904                   startIndex = 1;
 1905               }
 1906   
 1907               if ('a' == Character.toLowerCase(currentConversionType)) {
 1908                   startIndex += 2;
 1909               }
 1910               return padding(result, startIndex);
 1911           }
 1912   
 1913           /*
 1914            * Transforms a Date to a formatted string.
 1915            */
 1916           private String transformFromDateTime() {
 1917               int startIndex = 0;
 1918               char currentConversionType = formatToken.getConversionType();
 1919   
 1920               if (formatToken.isPrecisionSet()) {
 1921                   throw new IllegalFormatPrecisionException(formatToken
 1922                           .getPrecision());
 1923               }
 1924   
 1925               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)) {
 1926                   throw new FormatFlagsConversionMismatchException(formatToken
 1927                           .getStrFlags(), currentConversionType);
 1928               }
 1929   
 1930               if (formatToken.isFlagSet(FormatToken.FLAG_MINUS)
 1931                       && FormatToken.UNSET == formatToken.getWidth()) {
 1932                   throw new MissingFormatWidthException("-" //$NON-NLS-1$
 1933                           + currentConversionType);
 1934               }
 1935   
 1936               if (null == arg) {
 1937                   return transformFromNull();
 1938               }
 1939   
 1940               Calendar calendar;
 1941               if (arg instanceof Calendar) {
 1942                   calendar = (Calendar) arg;
 1943               } else {
 1944                   Date date = null;
 1945                   if (arg instanceof Long) {
 1946                       date = new Date(((Long) arg).longValue());
 1947                   } else if (arg instanceof Date) {
 1948                       date = (Date) arg;
 1949                   } else {
 1950                       throw new IllegalFormatConversionException(
 1951                               currentConversionType, arg.getClass());
 1952                   }
 1953                   calendar = Calendar.getInstance(locale);
 1954                   calendar.setTime(date);
 1955               }
 1956   
 1957               if (null == dateTimeUtil) {
 1958                   dateTimeUtil = new DateTimeUtil(locale);
 1959               }
 1960               StringBuilder result = new StringBuilder();
 1961               // output result
 1962               dateTimeUtil.transform(formatToken, calendar, result);
 1963               return padding(result, startIndex);
 1964           }
 1965       }
 1966   
 1967       private static class FloatUtil {
 1968           private StringBuilder result;
 1969   
 1970           private DecimalFormat decimalFormat;
 1971   
 1972           private FormatToken formatToken;
 1973   
 1974           private Object argument;
 1975   
 1976           private char minusSign;
 1977   
 1978           FloatUtil(StringBuilder result, FormatToken formatToken,
 1979                   DecimalFormat decimalFormat, Object argument) {
 1980               this.result = result;
 1981               this.formatToken = formatToken;
 1982               this.decimalFormat = decimalFormat;
 1983               this.argument = argument;
 1984               this.minusSign = decimalFormat.getDecimalFormatSymbols()
 1985                       .getMinusSign();
 1986           }
 1987   
 1988           void transform(FormatToken aFormatToken, StringBuilder aResult) {
 1989               this.result = aResult;
 1990               this.formatToken = aFormatToken;
 1991               switch (formatToken.getConversionType()) {
 1992                   case 'e':
 1993                   case 'E': {
 1994                       transform_e();
 1995                       break;
 1996                   }
 1997                   case 'f': {
 1998                       transform_f();
 1999                       break;
 2000                   }
 2001                   case 'g':
 2002                   case 'G': {
 2003                       transform_g();
 2004                       break;
 2005                   }
 2006                   case 'a':
 2007                   case 'A': {
 2008                       transform_a();
 2009                       break;
 2010                   }
 2011                   default: {
 2012                       throw new UnknownFormatConversionException(String
 2013                               .valueOf(formatToken.getConversionType()));
 2014                   }
 2015               }
 2016           }
 2017   
 2018           char getMinusSign() {
 2019               return minusSign;
 2020           }
 2021   
 2022           char getAddSign() {
 2023               return '+';
 2024           }
 2025   
 2026           void transform_e() {
 2027               StringBuilder pattern = new StringBuilder();
 2028               pattern.append('0');
 2029               if (formatToken.getPrecision() > 0) {
 2030                   pattern.append('.');
 2031                   char[] zeros = new char[formatToken.getPrecision()];
 2032                   Arrays.fill(zeros, '0');
 2033                   pattern.append(zeros);
 2034               }
 2035               pattern.append('E');
 2036               pattern.append("+00"); //$NON-NLS-1$
 2037               decimalFormat.applyPattern(pattern.toString());
 2038               String formattedString = decimalFormat.format(argument);
 2039               result.append(formattedString.replace('E', 'e'));
 2040   
 2041               // if the flag is sharp and decimal seperator is always given
 2042               // out.
 2043               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)
 2044                       && 0 == formatToken.getPrecision()) {
 2045                   int indexOfE = result.indexOf("e"); //$NON-NLS-1$
 2046                   char dot = decimalFormat.getDecimalFormatSymbols()
 2047                           .getDecimalSeparator();
 2048                   result.insert(indexOfE, dot);
 2049               }
 2050           }
 2051   
 2052           void transform_g() {
 2053               int precision = formatToken.getPrecision();
 2054               precision = (0 == precision ? 1 : precision);
 2055               formatToken.setPrecision(precision);
 2056   
 2057               if (0.0 == ((Number) argument).doubleValue()) {
 2058                   precision--;
 2059                   formatToken.setPrecision(precision);
 2060                   transform_f();
 2061                   return;
 2062               }
 2063   
 2064               boolean requireScientificRepresentation = true;
 2065               double d = ((Number) argument).doubleValue();
 2066               d = Math.abs(d);
 2067               if (Double.isInfinite(d)) {
 2068                   precision = formatToken.getPrecision();
 2069                   precision--;
 2070                   formatToken.setPrecision(precision);
 2071                   transform_e();
 2072                   return;
 2073               }
 2074               BigDecimal b = new BigDecimal(d, new MathContext(precision));
 2075               d = b.doubleValue();
 2076               long l = b.longValue();
 2077   
 2078               if (d >= 1 && d < Math.pow(10, precision)) {
 2079                   if (l < Math.pow(10, precision)) {
 2080                       requireScientificRepresentation = false;
 2081                       precision -= String.valueOf(l).length();
 2082                       precision = precision < 0 ? 0 : precision;
 2083                       l = Math.round(d * Math.pow(10, precision + 1));
 2084                       if (String.valueOf(l).length() <= formatToken
 2085                               .getPrecision()) {
 2086                           precision++;
 2087                       }
 2088                       formatToken.setPrecision(precision);
 2089                   }
 2090   
 2091               } else {
 2092                   l = b.movePointRight(4).longValue();
 2093                   if (d >= Math.pow(10, -4) && d < 1) {
 2094                       requireScientificRepresentation = false;
 2095                       precision += 4 - String.valueOf(l).length();
 2096                       l = b.movePointRight(precision + 1).longValue();
 2097                       if (String.valueOf(l).length() <= formatToken
 2098                               .getPrecision()) {
 2099                           precision++;
 2100                       }
 2101                       l = b.movePointRight(precision).longValue();
 2102                       if (l >= Math.pow(10, precision - 4)) {
 2103                           formatToken.setPrecision(precision);
 2104                       }
 2105                   }
 2106               }
 2107               if (requireScientificRepresentation) {
 2108                   precision = formatToken.getPrecision();
 2109                   precision--;
 2110                   formatToken.setPrecision(precision);
 2111                   transform_e();
 2112               } else {
 2113                   transform_f();
 2114               }
 2115   
 2116           }
 2117   
 2118           void transform_f() {
 2119               StringBuilder pattern = new StringBuilder();
 2120               if (formatToken.isFlagSet(FormatToken.FLAG_COMMA)) {
 2121                   pattern.append(',');
 2122                   int groupingSize = decimalFormat.getGroupingSize();
 2123                   if (groupingSize > 1) {
 2124                       char[] sharps = new char[groupingSize - 1];
 2125                       Arrays.fill(sharps, '#');
 2126                       pattern.append(sharps);
 2127                   }
 2128               }
 2129   
 2130               pattern.append(0);
 2131   
 2132               if (formatToken.getPrecision() > 0) {
 2133                   pattern.append('.');
 2134                   char[] zeros = new char[formatToken.getPrecision()];
 2135                   Arrays.fill(zeros, '0');
 2136                   pattern.append(zeros);
 2137               }
 2138               decimalFormat.applyPattern(pattern.toString());
 2139               result.append(decimalFormat.format(argument));
 2140               // if the flag is sharp and decimal seperator is always given
 2141               // out.
 2142               if (formatToken.isFlagSet(FormatToken.FLAG_SHARP)
 2143                       && 0 == formatToken.getPrecision()) {
 2144                   char dot = decimalFormat.getDecimalFormatSymbols()
 2145                           .getDecimalSeparator();
 2146                   result.append(dot);
 2147               }
 2148   
 2149           }
 2150   
 2151           void transform_a() {
 2152               char currentConversionType = formatToken.getConversionType();
 2153   
 2154               if (argument instanceof Float) {
 2155                   Float F = (Float) argument;
 2156                   result.append(Float.toHexString(F.floatValue()));
 2157   
 2158               } else if (argument instanceof Double) {
 2159                   Double D = (Double) argument;
 2160                   result.append(Double.toHexString(D.doubleValue()));
 2161               } else {
 2162                   // BigInteger is not supported.
 2163                   throw new IllegalFormatConversionException(
 2164                           currentConversionType, argument.getClass());
 2165               }
 2166   
 2167               if (!formatToken.isPrecisionSet()) {
 2168                   return;
 2169               }
 2170   
 2171               int precision = formatToken.getPrecision();
 2172               precision = (0 == precision ? 1 : precision);
 2173               int indexOfFirstFracitoanlDigit = result.indexOf(".") + 1; //$NON-NLS-1$
 2174               int indexOfP = result.indexOf("p"); //$NON-NLS-1$
 2175               int fractionalLength = indexOfP - indexOfFirstFracitoanlDigit;
 2176   
 2177               if (fractionalLength == precision) {
 2178                   return;
 2179               }
 2180   
 2181               if (fractionalLength < precision) {
 2182                   char zeros[] = new char[precision - fractionalLength];
 2183                   Arrays.fill(zeros, '0');
 2184                   result.insert(indexOfP, zeros);
 2185                   return;
 2186               }
 2187               result.delete(indexOfFirstFracitoanlDigit + precision, indexOfP);
 2188           }
 2189       }
 2190   
 2191       private static class DateTimeUtil {
 2192           private Calendar calendar;
 2193   
 2194           private Locale locale;
 2195   
 2196           private StringBuilder result;
 2197   
 2198           private DateFormatSymbols dateFormatSymbols;
 2199   
 2200           DateTimeUtil(Locale locale) {
 2201               this.locale = locale;
 2202           }
 2203   
 2204           void transform(FormatToken formatToken, Calendar aCalendar,
 2205                   StringBuilder aResult) {
 2206               this.result = aResult;
 2207               this.calendar = aCalendar;
 2208               char suffix = formatToken.getDateSuffix();
 2209   
 2210               switch (suffix) {
 2211                   case 'H': {
 2212                       transform_H();
 2213                       break;
 2214                   }
 2215                   case 'I': {
 2216                       transform_I();
 2217                       break;
 2218                   }
 2219                   case 'M': {
 2220                       transform_M();
 2221                       break;
 2222                   }
 2223                   case 'S': {
 2224                       transform_S();
 2225                       break;
 2226                   }
 2227                   case 'L': {
 2228                       transform_L();
 2229                       break;
 2230                   }
 2231                   case 'N': {
 2232                       transform_N();
 2233                       break;
 2234                   }
 2235                   case 'k': {
 2236                       transform_k();
 2237                       break;
 2238                   }
 2239                   case 'l': {
 2240                       transform_l();
 2241                       break;
 2242                   }
 2243                   case 'p': {
 2244                       transform_p(true);
 2245                       break;
 2246                   }
 2247                   case 's': {
 2248                       transform_s();
 2249                       break;
 2250                   }
 2251                   case 'z': {
 2252                       transform_z();
 2253                       break;
 2254                   }
 2255                   case 'Z': {
 2256                       transform_Z();
 2257                       break;
 2258                   }
 2259                   case 'Q': {
 2260                       transform_Q();
 2261                       break;
 2262                   }
 2263                   case 'B': {
 2264                       transform_B();
 2265                       break;
 2266                   }
 2267                   case 'b':
 2268                   case 'h': {
 2269                       transform_b();
 2270                       break;
 2271                   }
 2272                   case 'A': {
 2273                       transform_A();
 2274                       break;
 2275                   }
 2276                   case 'a': {
 2277                       transform_a();
 2278                       break;
 2279                   }
 2280                   case 'C': {
 2281                       transform_C();
 2282                       break;
 2283                   }
 2284                   case 'Y': {
 2285                       transform_Y();
 2286                       break;
 2287                   }
 2288                   case 'y': {
 2289                       transform_y();
 2290                       break;
 2291                   }
 2292                   case 'j': {
 2293                       transform_j();
 2294                       break;
 2295                   }
 2296                   case 'm': {
 2297                       transform_m();
 2298                       break;
 2299                   }
 2300                   case 'd': {
 2301                       transform_d();
 2302                       break;
 2303                   }
 2304                   case 'e': {
 2305                       transform_e();
 2306                       break;
 2307                   }
 2308                   case 'R': {
 2309                       transform_R();
 2310                       break;
 2311                   }
 2312   
 2313                   case 'T': {
 2314                       transform_T();
 2315                       break;
 2316                   }
 2317                   case 'r': {
 2318                       transform_r();
 2319                       break;
 2320                   }
 2321                   case 'D': {
 2322                       transform_D();
 2323                       break;
 2324                   }
 2325                   case 'F': {
 2326                       transform_F();
 2327                       break;
 2328                   }
 2329                   case 'c': {
 2330                       transform_c();
 2331                       break;
 2332                   }
 2333                   default: {
 2334                       throw new UnknownFormatConversionException(String
 2335                               .valueOf(formatToken.getConversionType())
 2336                               + formatToken.getDateSuffix());
 2337                   }
 2338               }
 2339           }
 2340   
 2341           private void transform_e() {
 2342               int day = calendar.get(Calendar.DAY_OF_MONTH);
 2343               result.append(day);
 2344           }
 2345   
 2346           private void transform_d() {
 2347               int day = calendar.get(Calendar.DAY_OF_MONTH);
 2348               result.append(paddingZeros(day, 2));
 2349           }
 2350   
 2351           private void transform_m() {
 2352               int month = calendar.get(Calendar.MONTH);
 2353               // The returned month starts from zero, which needs to be
 2354               // incremented by 1.
 2355               month++;
 2356               result.append(paddingZeros(month, 2));
 2357           }
 2358   
 2359           private void transform_j() {
 2360               int day = calendar.get(Calendar.DAY_OF_YEAR);
 2361               result.append(paddingZeros(day, 3));
 2362           }
 2363   
 2364           private void transform_y() {
 2365               int year = calendar.get(Calendar.YEAR);
 2366               year %= 100;
 2367               result.append(paddingZeros(year, 2));
 2368           }
 2369   
 2370           private void transform_Y() {
 2371               int year = calendar.get(Calendar.YEAR);
 2372               result.append(paddingZeros(year, 4));
 2373           }
 2374   
 2375           private void transform_C() {
 2376               int year = calendar.get(Calendar.YEAR);
 2377               year /= 100;
 2378               result.append(paddingZeros(year, 2));
 2379           }
 2380   
 2381           private void transform_a() {
 2382               int day = calendar.get(Calendar.DAY_OF_WEEK);
 2383               result.append(getDateFormatSymbols().getShortWeekdays()[day]);
 2384           }
 2385   
 2386           private void transform_A() {
 2387               int day = calendar.get(Calendar.DAY_OF_WEEK);
 2388               result.append(getDateFormatSymbols().getWeekdays()[day]);
 2389           }
 2390   
 2391           private void transform_b() {
 2392               int month = calendar.get(Calendar.MONTH);
 2393               result.append(getDateFormatSymbols().getShortMonths()[month]);
 2394           }
 2395   
 2396           private void transform_B() {
 2397               int month = calendar.get(Calendar.MONTH);
 2398               result.append(getDateFormatSymbols().getMonths()[month]);
 2399           }
 2400   
 2401           private void transform_Q() {
 2402               long milliSeconds = calendar.getTimeInMillis();
 2403               result.append(milliSeconds);
 2404           }
 2405   
 2406           private void transform_s() {
 2407               long milliSeconds = calendar.getTimeInMillis();
 2408               milliSeconds /= 1000;
 2409               result.append(milliSeconds);
 2410           }
 2411   
 2412           private void transform_Z() {
 2413               TimeZone timeZone = calendar.getTimeZone();
 2414               result.append(timeZone
 2415                       .getDisplayName(
 2416                               timeZone.inDaylightTime(calendar.getTime()),
 2417                               TimeZone.SHORT, locale));
 2418           }
 2419   
 2420           private void transform_z() {
 2421               int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
 2422               zoneOffset /= 3600000;
 2423               zoneOffset *= 100;
 2424               if (zoneOffset >= 0) {
 2425                   result.append('+');
 2426               }
 2427               result.append(paddingZeros(zoneOffset, 4));
 2428           }
 2429   
 2430           private void transform_p(boolean isLowerCase) {
 2431               int i = calendar.get(Calendar.AM_PM);
 2432               String s = getDateFormatSymbols().getAmPmStrings()[i];
 2433               if (isLowerCase) {
 2434                   s = s.toLowerCase(locale);
 2435               }
 2436               result.append(s);
 2437           }
 2438   
 2439           private void transform_N() {
 2440               // TODO System.nanoTime();
 2441               long nanosecond = calendar.get(Calendar.MILLISECOND) * 1000000L;
 2442               result.append(paddingZeros(nanosecond, 9));
 2443           }
 2444   
 2445           private void transform_L() {
 2446               int millisecond = calendar.get(Calendar.MILLISECOND);
 2447               result.append(paddingZeros(millisecond, 3));
 2448           }
 2449   
 2450           private void transform_S() {
 2451               int second = calendar.get(Calendar.SECOND);
 2452               result.append(paddingZeros(second, 2));
 2453           }
 2454   
 2455           private void transform_M() {
 2456               int minute = calendar.get(Calendar.MINUTE);
 2457               result.append(paddingZeros(minute, 2));
 2458           }
 2459   
 2460           private void transform_l() {
 2461               int hour = calendar.get(Calendar.HOUR);
 2462               if (0 == hour) {
 2463                   hour = 12;
 2464               }
 2465               result.append(hour);
 2466           }
 2467   
 2468           private void transform_k() {
 2469               int hour = calendar.get(Calendar.HOUR_OF_DAY);
 2470               result.append(hour);
 2471           }
 2472   
 2473           private void transform_I() {
 2474               int hour = calendar.get(Calendar.HOUR);
 2475               if (0 == hour) {
 2476                   hour = 12;
 2477               }
 2478               result.append(paddingZeros(hour, 2));
 2479           }
 2480   
 2481           private void transform_H() {
 2482               int hour = calendar.get(Calendar.HOUR_OF_DAY);
 2483               result.append(paddingZeros(hour, 2));
 2484           }
 2485   
 2486           private void transform_R() {
 2487               transform_H();
 2488               result.append(':');
 2489               transform_M();
 2490           }
 2491   
 2492           private void transform_T() {
 2493               transform_H();
 2494               result.append(':');
 2495               transform_M();
 2496               result.append(':');
 2497               transform_S();
 2498           }
 2499   
 2500           private void transform_r() {
 2501               transform_I();
 2502               result.append(':');
 2503               transform_M();
 2504               result.append(':');
 2505               transform_S();
 2506               result.append(' ');
 2507               transform_p(false);
 2508           }
 2509   
 2510           private void transform_D() {
 2511               transform_m();
 2512               result.append('/');
 2513               transform_d();
 2514               result.append('/');
 2515               transform_y();
 2516           }
 2517   
 2518           private void transform_F() {
 2519               transform_Y();
 2520               result.append('-');
 2521               transform_m();
 2522               result.append('-');
 2523               transform_d();
 2524           }
 2525   
 2526           private void transform_c() {
 2527               transform_a();
 2528               result.append(' ');
 2529               transform_b();
 2530               result.append(' ');
 2531               transform_d();
 2532               result.append(' ');
 2533               transform_T();
 2534               result.append(' ');
 2535               transform_Z();
 2536               result.append(' ');
 2537               transform_Y();
 2538           }
 2539   
 2540           private static String paddingZeros(long number, int length) {
 2541               int len = length;
 2542               StringBuilder result = new StringBuilder();
 2543               result.append(number);
 2544               int startIndex = 0;
 2545               if (number < 0) {
 2546                   len++;
 2547                   startIndex = 1;
 2548               }
 2549               len -= result.length();
 2550               if (len > 0) {
 2551                   char[] zeros = new char[len];
 2552                   Arrays.fill(zeros, '0');
 2553                   result.insert(startIndex, zeros);
 2554               }
 2555               return result.toString();
 2556           }
 2557   
 2558           private DateFormatSymbols getDateFormatSymbols() {
 2559               if (null == dateFormatSymbols) {
 2560                   dateFormatSymbols = new DateFormatSymbols(locale);
 2561               }
 2562               return dateFormatSymbols;
 2563           }
 2564       }
 2565   
 2566       private static class ParserStateMachine {
 2567   
 2568           private static final char EOS = (char) -1;
 2569   
 2570           private static final int EXIT_STATE = 0;
 2571   
 2572           private static final int ENTRY_STATE = 1;
 2573   
 2574           private static final int START_CONVERSION_STATE = 2;
 2575   
 2576           private static final int FLAGS_STATE = 3;
 2577   
 2578           private static final int WIDTH_STATE = 4;
 2579   
 2580           private static final int PRECISION_STATE = 5;
 2581   
 2582           private static final int CONVERSION_TYPE_STATE = 6;
 2583   
 2584           private static final int SUFFIX_STATE = 7;
 2585   
 2586           private FormatToken token;
 2587   
 2588           private int state = ENTRY_STATE;
 2589   
 2590           private char currentChar = 0;
 2591   
 2592           private CharBuffer format = null;
 2593   
 2594           ParserStateMachine(CharBuffer format) {
 2595               this.format = format;
 2596           }
 2597   
 2598           void reset() {
 2599               this.currentChar = (char) FormatToken.UNSET;
 2600               this.state = ENTRY_STATE;
 2601               this.token = null;
 2602           }
 2603   
 2604           /*
 2605            * Gets the information about the current format token. Information is
 2606            * recorded in the FormatToken returned and the position of the stream
 2607            * for the format string will be advanced till the next format token.
 2608            */
 2609           FormatToken getNextFormatToken() {
 2610               token = new FormatToken();
 2611               token.setFormatStringStartIndex(format.position());
 2612   
 2613               // FINITE AUTOMATIC MACHINE
 2614               while (true) {
 2615   
 2616                   if (ParserStateMachine.EXIT_STATE != state) {
 2617                       // exit state does not need to get next char
 2618                       currentChar = getNextFormatChar();
 2619                       if (EOS == currentChar
 2620                               && ParserStateMachine.ENTRY_STATE != state) {
 2621                           throw new UnknownFormatConversionException(
 2622                                   getFormatString());
 2623                       }
 2624                   }
 2625   
 2626                   switch (state) {
 2627                       // exit state
 2628                       case ParserStateMachine.EXIT_STATE: {
 2629                           process_EXIT_STATE();
 2630                           return token;
 2631                       }
 2632                           // plain text state, not yet applied converter
 2633                       case ParserStateMachine.ENTRY_STATE: {
 2634                           process_ENTRY_STATE();
 2635                           break;
 2636                       }
 2637                           // begins converted string
 2638                       case ParserStateMachine.START_CONVERSION_STATE: {
 2639                           process_START_CONVERSION_STATE();
 2640                           break;
 2641                       }
 2642                       case ParserStateMachine.FLAGS_STATE: {
 2643                           process_FlAGS_STATE();
 2644                           break;
 2645                       }
 2646                       case ParserStateMachine.WIDTH_STATE: {
 2647                           process_WIDTH_STATE();
 2648                           break;
 2649                       }
 2650                       case ParserStateMachine.PRECISION_STATE: {
 2651                           process_PRECISION_STATE();
 2652                           break;
 2653                       }
 2654                       case ParserStateMachine.CONVERSION_TYPE_STATE: {
 2655                           process_CONVERSION_TYPE_STATE();
 2656                           break;
 2657                       }
 2658                       case ParserStateMachine.SUFFIX_STATE: {
 2659                           process_SUFFIX_STATE();
 2660                           break;
 2661                       }
 2662                   }
 2663               }
 2664           }
 2665   
 2666           /*
 2667            * Gets next char from the format string.
 2668            */
 2669           private char getNextFormatChar() {
 2670               if (format.hasRemaining()) {
 2671                   return format.get();
 2672               }
 2673               return EOS;
 2674           }
 2675   
 2676           private String getFormatString() {
 2677               int end = format.position();
 2678               format.rewind();
 2679               String formatString = format.subSequence(
 2680                       token.getFormatStringStartIndex(), end).toString();
 2681               format.position(end);
 2682               return formatString;
 2683           }
 2684   
 2685           private void process_ENTRY_STATE() {
 2686               if (EOS == currentChar) {
 2687                   state = ParserStateMachine.EXIT_STATE;
 2688               } else if ('%' == currentChar) {
 2689                   // change to conversion type state
 2690                   state = START_CONVERSION_STATE;
 2691               }
 2692               // else remains in ENTRY_STATE
 2693           }
 2694   
 2695           private void process_START_CONVERSION_STATE() {
 2696               if (Character.isDigit(currentChar)) {
 2697                   int position = format.position() - 1;
 2698                   int number = parseInt(format);
 2699                   char nextChar = 0;
 2700                   if (format.hasRemaining()) {
 2701                       nextChar = format.get();
 2702                   }
 2703                   if ('$' == nextChar) {
 2704                       // the digital sequence stands for the argument
 2705                       // index.
 2706                       int argIndex = number;
 2707                       // k$ stands for the argument whose index is k-1 except that
 2708                       // 0$ and 1$ both stands for the first element.
 2709                       if (argIndex > 0) {
 2710                           token.setArgIndex(argIndex - 1);
 2711                       } else if (argIndex == FormatToken.UNSET) {
 2712                           throw new MissingFormatArgumentException(
 2713                                   getFormatString());
 2714                       }
 2715                       state = FLAGS_STATE;
 2716                   } else {
 2717                       // the digital zero stands for one format flag.
 2718                       if ('0' == currentChar) {
 2719                           state = FLAGS_STATE;
 2720                           format.position(position);
 2721                       } else {
 2722                           // the digital sequence stands for the width.
 2723                           state = WIDTH_STATE;
 2724                           // do not get the next char.
 2725                           format.position(format.position() - 1);
 2726                           token.setWidth(number);
 2727                       }
 2728                   }
 2729                   currentChar = nextChar;
 2730               } else if ('<' == currentChar) {
 2731                   state = FLAGS_STATE;
 2732                   token.setArgIndex(FormatToken.LAST_ARGUMENT_INDEX);
 2733               } else {
 2734                   state = FLAGS_STATE;
 2735                   // do not get the next char.
 2736                   format.position(format.position() - 1);
 2737               }
 2738   
 2739           }
 2740   
 2741           private void process_FlAGS_STATE() {
 2742               if (token.setFlag(currentChar)) {
 2743                   // remains in FLAGS_STATE
 2744               } else if (Character.isDigit(currentChar)) {
 2745                   token.setWidth(parseInt(format));
 2746                   state = WIDTH_STATE;
 2747               } else if ('.' == currentChar) {
 2748                   state = PRECISION_STATE;
 2749               } else {
 2750                   state = CONVERSION_TYPE_STATE;
 2751                   // do not get the next char.
 2752                   format.position(format.position() - 1);
 2753               }
 2754           }
 2755   
 2756           private void process_WIDTH_STATE() {
 2757               if ('.' == currentChar) {
 2758                   state = PRECISION_STATE;
 2759               } else {
 2760                   state = CONVERSION_TYPE_STATE;
 2761                   // do not get the next char.
 2762                   format.position(format.position() - 1);
 2763               }
 2764           }
 2765   
 2766           private void process_PRECISION_STATE() {
 2767               if (Character.isDigit(currentChar)) {
 2768                   token.setPrecision(parseInt(format));
 2769               } else {
 2770                   // the precision is required but not given by the
 2771                   // format string.
 2772                   throw new UnknownFormatConversionException(getFormatString());
 2773               }
 2774               state = CONVERSION_TYPE_STATE;
 2775           }
 2776   
 2777           private void process_CONVERSION_TYPE_STATE() {
 2778               token.setConversionType(currentChar);
 2779               if ('t' == currentChar || 'T' == currentChar) {
 2780                   state = SUFFIX_STATE;
 2781               } else {
 2782                   state = EXIT_STATE;
 2783               }
 2784   
 2785           }
 2786   
 2787           private void process_SUFFIX_STATE() {
 2788               token.setDateSuffix(currentChar);
 2789               state = EXIT_STATE;
 2790           }
 2791   
 2792           private void process_EXIT_STATE() {
 2793               token.setPlainText(getFormatString());
 2794           }
 2795   
 2796           /*
 2797            * Parses integer value from the given buffer
 2798            */
 2799           private int parseInt(CharBuffer buffer) {
 2800               int start = buffer.position() - 1;
 2801               int end = buffer.limit();
 2802               while (buffer.hasRemaining()) {
 2803                   if (!Character.isDigit(buffer.get())) {
 2804                       end = buffer.position() - 1;
 2805                       break;
 2806                   }
 2807               }
 2808               buffer.position(0);
 2809               String intStr = buffer.subSequence(start, end).toString();
 2810               buffer.position(end);
 2811               try {
 2812                   return Integer.parseInt(intStr);
 2813               } catch (NumberFormatException e) {
 2814                   return FormatToken.UNSET;
 2815               }
 2816           }
 2817       }
 2818   }

Home » openjdk-7 » java » util » [javadoc | source]