//
// (c) 2000 Sun Microsystems, Inc.
// ALL RIGHTS RESERVED
// 
// License Grant-
// 
// 
// Permission to use, copy, modify, and distribute this Software and its
// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
// hereby granted.
// 
// This Software is provided "AS IS". All express warranties, including any
// implied warranty of merchantability, satisfactory quality, fitness for a
// particular purpose, or non-infringement, are disclaimed, except to the extent
// that such disclaimers are held to be legally invalid.
// 
// You acknowledge that Software is not designed, licensed or intended for use in
// the design, construction, operation or maintenance of any nuclear facility
// ("High Risk Activities"). Sun disclaims any express or implied warranty of
// fitness for such uses.
//
// Please refer to the file http://www.sun.com/policies/trademarks/ for further
// important trademark information and to
// http://java.sun.com/nav/business/index.html for further important licensing
// information for the Java Technology.
//
package routines.system;

import java.util.Enumeration;
import java.util.Vector;
import java.util.Locale;
import java.text.DecimalFormatSymbols;

/**
 * PrintfFormat allows the formatting of an array of objects embedded within a string. Primitive types must be passed
 * using wrapper types. The formatting is controlled by a control string.
 * <p>
 * A control string is a Java string that contains a control specification. The control specification starts at the
 * first percent sign (%) in the string, provided that this percent sign
 * <ol>
 * <li>is not escaped protected by a matching % or is not an escape % character,
 * <li>is not at the end of the format string, and
 * <li>precedes a sequence of characters that parses as a valid control specification.
 * </ol>
 * </p>
 * <p>
 * A control specification usually takes the form:
 * 
 * <pre>
 *  % ['-+ #0]* [0..9]* { . [0..9]* }+
 *                 { [hlL] }+ [idfgGoxXeEcs]
 * </pre>
 * 
 * There are variants of this basic form that are discussed below.
 * </p>
 * <p>
 * The format is composed of zero or more directives defined as follows:
 * <ul>
 * <li>ordinary characters, which are simply copied to the output stream;
 * <li>escape sequences, which represent non-graphic characters; and
 * <li>conversion specifications, each of which results in the fetching of zero or more arguments.
 * </ul>
 * </p>
 * <p>
 * The results are undefined if there are insufficient arguments for the format. Usually an unchecked exception will be
 * thrown. If the format is exhausted while arguments remain, the excess arguments are evaluated but are otherwise
 * ignored. In format strings containing the % form of conversion specifications, each argument in the argument list is
 * used exactly once.
 * </p>
 * <p>
 * Conversions can be applied to the <code>n</code>th argument after the format in the argument list, rather than to
 * the next unused argument. In this case, the conversion characer % is replaced by the sequence %<code>n</code>$,
 * where <code>n</code> is a decimal integer giving the position of the argument in the argument list.
 * </p>
 * <p>
 * In format strings containing the %<code>n</code>$ form of conversion specifications, each argument in the
 * argument list is used exactly once.
 * </p>
 * 
 * <h4>Escape Sequences</h4>
 * <p>
 * The following table lists escape sequences and associated actions on display devices capable of the action. <table>
 * <tr>
 * <th align=left>Sequence</th>
 * <th align=left>Name</th>
 * <th align=left>Description</th>
 * </tr>
 * <tr>
 * <td>\\</td>
 * <td>backlash</td>
 * <td>None. </td>
 * </tr>
 * <tr>
 * <td>\a</td>
 * <td>alert</td>
 * <td>Attempts to alert the user through audible or visible notification. </td>
 * </tr>
 * <tr>
 * <td>\b</td>
 * <td>backspace</td>
 * <td>Moves the printing position to one column before the current position, unless the current position is the start
 * of a line. </td>
 * </tr>
 * <tr>
 * <td>\f</td>
 * <td>form-feed</td>
 * <td>Moves the printing position to the initial printing position of the next logical page. </td>
 * </tr>
 * <tr>
 * <td>\n</td>
 * <td>newline</td>
 * <td>Moves the printing position to the start of the next line. </td>
 * </tr>
 * <tr>
 * <td>\r</td>
 * <td>carriage-return</td>
 * <td>Moves the printing position to the start of the current line. </td>
 * </tr>
 * <tr>
 * <td>\t</td>
 * <td>tab</td>
 * <td>Moves the printing position to the next implementation- defined horizontal tab position. </td>
 * </tr>
 * <tr>
 * <td>\v</td>
 * <td>vertical-tab</td>
 * <td>Moves the printing position to the start of the next implementation-defined vertical tab position. </td>
 * </tr>
 * </table>
 * </p>
 * <h4>Conversion Specifications</h4>
 * <p>
 * Each conversion specification is introduced by the percent sign character (%). After the character %, the following
 * appear in sequence:
 * </p>
 * <p>
 * Zero or more flags (in any order), which modify the meaning of the conversion specification.
 * </p>
 * <p>
 * An optional minimum field width. If the converted value has fewer characters than the field width, it will be padded
 * with spaces by default on the left; t will be padded on the right, if the left- adjustment flag (-), described below,
 * is given to the field width. The field width takes the form of a decimal integer. If the conversion character is s,
 * the field width is the the minimum number of characters to be printed.
 * </p>
 * <p>
 * An optional precision that gives the minumum number of digits to appear for the d, i, o, x or X conversions (the
 * field is padded with leading zeros); the number of digits to appear after the radix character for the e, E, and f
 * conversions, the maximum number of significant digits for the g and G conversions; or the maximum number of
 * characters to be written from a string is s and S conversions. The precision takes the form of an optional decimal
 * digit string, where a null digit string is treated as 0. If a precision appears with a c conversion character the
 * precision is ignored.
 * </p>
 * <p>
 * An optional h specifies that a following d, i, o, x, or X conversion character applies to a type short argument (the
 * argument will be promoted according to the integral promotions and its value converted to type short before
 * printing).
 * </p>
 * <p>
 * An optional l (ell) specifies that a following d, i, o, x, or X conversion character applies to a type long argument.
 * </p>
 * <p>
 * A field width or precision may be indicated by an asterisk (*) instead of a digit string. In this case, an integer
 * argument supplised the field width precision. The argument that is actually converted is not fetched until the
 * conversion letter is seen, so the the arguments specifying field width or precision must appear before the argument
 * (if any) to be converted. If the precision argument is negative, it will be changed to zero. A negative field width
 * argument is taken as a - flag, followed by a positive field width.
 * </p>
 * <p>
 * In format strings containing the %<code>n</code>$ form of a conversion specification, a field width or precision
 * may be indicated by the sequence *<code>m</code>$, where m is a decimal integer giving the position in the
 * argument list (after the format argument) of an integer argument containing the field width or precision.
 * </p>
 * <p>
 * The format can contain either numbered argument specifications (that is, %<code>n</code>$ and *<code>m</code>$),
 * or unnumbered argument specifications (that is % and *), but normally not both. The only exception to this is that %%
 * can be mixed with the %<code>n</code>$ form. The results of mixing numbered and unnumbered argument
 * specifications in a format string are undefined.
 * </p>
 * 
 * <h4>Flag Characters</h4>
 * <p>
 * The flags and their meanings are:
 * </p>
 * <dl>
 * <dt>'
 * <dd> integer portion of the result of a decimal conversion (%i, %d, %f, %g, or %G) will be formatted with thousands'
 * grouping characters. For other conversions the flag is ignored. The non-monetary grouping character is used.
 * <dt>-
 * <dd> result of the conversion is left-justified within the field. (It will be right-justified if this flag is not
 * specified).</td>
 * </tr>
 * <dt>+
 * <dd> result of a signed conversion always begins with a sign (+ or -). (It will begin with a sign only when a
 * negative value is converted if this flag is not specified.)
 * <dt>&lt;space&gt;
 * <dd> If the first character of a signed conversion is not a sign, a space character will be placed before the result.
 * This means that if the space character and + flags both appear, the space flag will be ignored.
 * <dt>#
 * <dd> value is to be converted to an alternative form. For c, d, i, and s conversions, the flag has no effect. For o
 * conversion, it increases the precision to force the first digit of the result to be a zero. For x or X conversion, a
 * non-zero result has 0x or 0X prefixed to it, respectively. For e, E, f, g, and G conversions, the result always
 * contains a radix character, even if no digits follow the radix character (normally, a decimal point appears in the
 * result of these conversions only if a digit follows it). For g and G conversions, trailing zeros will not be removed
 * from the result as they normally are.
 * <dt>0
 * <dd> d, i, o, x, X, e, E, f, g, and G conversions, leading zeros (following any indication of sign or base) are used
 * to pad to the field width; no space padding is performed. If the 0 and - flags both appear, the 0 flag is ignored.
 * For d, i, o, x, and X conversions, if a precision is specified, the 0 flag will be ignored. For c conversions, the
 * flag is ignored.
 * </dl>
 * 
 * <h4>Conversion Characters</h4>
 * <p>
 * Each conversion character results in fetching zero or more arguments. The results are undefined if there are
 * insufficient arguments for the format. Usually, an unchecked exception will be thrown. If the format is exhausted
 * while arguments remain, the excess arguments are ignored.
 * </p>
 * 
 * <p>
 * The conversion characters and their meanings are:
 * </p>
 * <dl>
 * <dt>d,i
 * <dd>The int argument is converted to a signed decimal in the style [-]dddd. The precision specifies the minimum
 * number of digits to appear; if the value being converted can be represented in fewer digits, it will be expanded with
 * leading zeros. The default precision is 1. The result of converting 0 with an explicit precision of 0 is no
 * characters.
 * <dt>o
 * <dd> The int argument is converted to unsigned octal format in the style ddddd. The precision specifies the minimum
 * number of digits to appear; if the value being converted can be represented in fewer digits, it will be expanded with
 * leading zeros. The default precision is 1. The result of converting 0 with an explicit precision of 0 is no
 * characters.
 * <dt>x
 * <dd> The int argument is converted to unsigned hexadecimal format in the style dddd; the letters abcdef are used. The
 * precision specifies the minimum numberof digits to appear; if the value being converted can be represented in fewer
 * digits, it will be expanded with leading zeros. The default precision is 1. The result of converting 0 with an
 * explicit precision of 0 is no characters.
 * <dt>X
 * <dd> Behaves the same as the x conversion character except that letters ABCDEF are used instead of abcdef.
 * <dt>f
 * <dd> The floating point number argument is written in decimal notation in the style [-]ddd.ddd, where the number of
 * digits after the radix character (shown here as a decimal point) is equal to the precision specification. A Locale is
 * used to determine the radix character to use in this format. If the precision is omitted from the argument, six
 * digits are written after the radix character; if the precision is explicitly 0 and the # flag is not specified, no
 * radix character appears. If a radix character appears, at least 1 digit appears before it. The value is rounded to
 * the appropriate number of digits.
 * <dt>e,E
 * <dd>The floating point number argument is written in the style [-]d.ddde{+-}dd (the symbols {+-} indicate either a
 * plus or minus sign), where there is one digit before the radix character (shown here as a decimal point) and the
 * number of digits after it is equal to the precision. A Locale is used to determine the radix character to use in this
 * format. When the precision is missing, six digits are written after the radix character; if the precision is 0 and
 * the # flag is not specified, no radix character appears. The E conversion will produce a number with E instead of e
 * introducing the exponent. The exponent always contains at least two digits. However, if the value to be written
 * requires an exponent greater than two digits, additional exponent digits are written as necessary. The value is
 * rounded to the appropriate number of digits.
 * <dt>g,G
 * <dd>The floating point number argument is written in style f or e (or in sytle E in the case of a G conversion
 * character), with the precision specifying the number of significant digits. If the precision is zero, it is taken as
 * one. The style used depends on the value converted: style e (or E) will be used only if the exponent resulting from
 * the conversion is less than -4 or greater than or equal to the precision. Trailing zeros are removed from the result.
 * A radix character appears only if it is followed by a digit.
 * <dt>c,C
 * <dd>The integer argument is converted to a char and the result is written.
 * 
 * <dt>s,S
 * <dd>The argument is taken to be a string and bytes from the string are written until the end of the string or the
 * number of bytes indicated by the precision specification of the argument is reached. If the precision is omitted from
 * the argument, it is taken to be infinite, so all characters up to the end of the string are written.
 * <dt>%
 * <dd>Write a % character; no argument is converted.
 * </dl>
 * <p>
 * If a conversion specification does not match one of the above forms, an IllegalArgumentException is thrown and the
 * instance of PrintfFormat is not created.
 * </p>
 * <p>
 * If a floating point value is the internal representation for infinity, the output is [+]Infinity, where Infinity is
 * either Infinity or Inf, depending on the desired output string length. Printing of the sign follows the rules
 * described above.
 * </p>
 * <p>
 * If a floating point value is the internal representation for "not-a-number," the output is [+]NaN. Printing of the
 * sign follows the rules described above.
 * </p>
 * <p>
 * In no case does a non-existent or small field width cause truncation of a field; if the result of a conversion is
 * wider than the field width, the field is simply expanded to contain the conversion result.
 * </p>
 * <p>
 * The behavior is like printf. One exception is that the minimum number of exponent digits is 3 instead of 2 for e and
 * E formats when the optional L is used before the e, E, g, or G conversion character. The optional L does not imply
 * conversion to a long long double.
 * </p>
 * <p>
 * The biggest divergence from the C printf specification is in the use of 16 bit characters. This allows the handling
 * of characters beyond the small ASCII character set and allows the utility to interoperate correctly with the rest of
 * the Java runtime environment.
 * </p>
 * <p>
 * Omissions from the C printf specification are numerous. All the known omissions are present because Java never uses
 * bytes to represent characters and does not have pointers:
 * </p>
 * <ul>
 * <li>%c is the same as %C.
 * <li>%s is the same as %S.
 * <li>u, p, and n conversion characters.
 * <li>%ws format.
 * <li>h modifier applied to an n conversion character.
 * <li>l (ell) modifier applied to the c, n, or s conversion characters.
 * <li>ll (ell ell) modifier to d, i, o, u, x, or X conversion characters.
 * <li>ll (ell ell) modifier to an n conversion character.
 * <li>c, C, d,i,o,u,x, and X conversion characters apply to Byte, Character, Short, Integer, Long types.
 * <li>f, e, E, g, and G conversion characters apply to Float and Double types.
 * <li>s and S conversion characters apply to String types.
 * <li>All other reference types can be formatted using the s or S conversion characters only.
 * </ul>
 * <p>
 * Most of this specification is quoted from the Unix man page for the sprintf utility.
 * </p>
 * 
 * @author Allan Jacobs
 * @version 1 Release 1: Initial release. Release 2: Asterisk field widths and precisions %n$ and *m$ Bug fixes g format
 * fix (2 digits in e form corrupt) rounding in f format implemented round up when digit not printed is 5 formatting of
 * -0.0f round up/down when last digits are 50000...
 */
public class PrintfFormat {

    /**
     * Constructs an array of control specifications possibly preceded, separated, or followed by ordinary strings.
     * Control strings begin with unpaired percent signs. A pair of successive percent signs designates a single percent
     * sign in the format.
     * 
     * @param fmtArg Control string.
     * @exception IllegalArgumentException if the control string is null, zero length, or otherwise malformed.
     */
    public PrintfFormat(String fmtArg) throws IllegalArgumentException {
        this(Locale.getDefault(), fmtArg);
    }

    /**
     * Constructs an array of control specifications possibly preceded, separated, or followed by ordinary strings.
     * Control strings begin with unpaired percent signs. A pair of successive percent signs designates a single percent
     * sign in the format.
     * 
     * @param fmtArg Control string.
     * @exception IllegalArgumentException if the control string is null, zero length, or otherwise malformed.
     */
    public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException {
        dfs = new DecimalFormatSymbols(locale);
        int ePos = 0;
        ConversionSpecification sFmt = null;
        String unCS = this.nonControl(fmtArg, 0);
        if (unCS != null) {
            sFmt = new ConversionSpecification();
            sFmt.setLiteral(unCS);
            vFmt.addElement(sFmt);
        }
        while (cPos != -1 && cPos < fmtArg.length()) {
            for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++) {
                char c = 0;
                c = fmtArg.charAt(ePos);
                if (c == 'i')
                    break;
                if (c == 'd')
                    break;
                if (c == 'f')
                    break;
                if (c == 'g')
                    break;
                if (c == 'G')
                    break;
                if (c == 'o')
                    break;
                if (c == 'x')
                    break;
                if (c == 'X')
                    break;
                if (c == 'e')
                    break;
                if (c == 'E')
                    break;
                if (c == 'c')
                    break;
                if (c == 's')
                    break;
                if (c == '%')
                    break;
            }
            ePos = Math.min(ePos + 1, fmtArg.length());
            sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos));
            vFmt.addElement(sFmt);
            unCS = this.nonControl(fmtArg, ePos);
            if (unCS != null) {
                sFmt = new ConversionSpecification();
                sFmt.setLiteral(unCS);
                vFmt.addElement(sFmt);
            }
        }
    }

    /**
     * Return a substring starting at <code>start</code> and ending at either the end of the String <code>s</code>,
     * the next unpaired percent sign, or at the end of the String if the last character is a percent sign.
     * 
     * @param s Control string.
     * @param start Position in the string <code>s</code> to begin looking for the start of a control string.
     * @return the substring from the start position to the beginning of the control string.
     */
    private String nonControl(String s, int start) {
        String ret = ""; //$NON-NLS-1$
        cPos = s.indexOf("%", start); //$NON-NLS-1$
        if (cPos == -1)
            cPos = s.length();
        return s.substring(start, cPos);
    }

    /**
     * Format an array of objects. Byte, Short, Integer, Long, Float, Double, and Character arguments are treated as
     * wrappers for primitive types.
     * 
     * @param o The array of objects to format.
     * @return The formatted String.
     */
    public String sprintf(Object[] o) {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        int i = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else {
                if (cs.isPositionalSpecification()) {
                    i = cs.getArgumentPosition() - 1;
                    if (cs.isPositionalFieldWidth()) {
                        int ifw = cs.getArgumentPositionForFieldWidth() - 1;
                        cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue());
                    }
                    if (cs.isPositionalPrecision()) {
                        int ipr = cs.getArgumentPositionForPrecision() - 1;
                        cs.setPrecisionWithArg(((Integer) o[ipr]).intValue());
                    }
                } else {
                    if (cs.isVariableFieldWidth()) {
                        cs.setFieldWidthWithArg(((Integer) o[i]).intValue());
                        i++;
                    }
                    if (cs.isVariablePrecision()) {
                        cs.setPrecisionWithArg(((Integer) o[i]).intValue());
                        i++;
                    }
                }
                if (o[i] instanceof Byte)
                    sb.append(cs.internalsprintf(((Byte) o[i]).byteValue()));
                else if (o[i] instanceof Short)
                    sb.append(cs.internalsprintf(((Short) o[i]).shortValue()));
                else if (o[i] instanceof Integer)
                    sb.append(cs.internalsprintf(((Integer) o[i]).intValue()));
                else if (o[i] instanceof Long)
                    sb.append(cs.internalsprintf(((Long) o[i]).longValue()));
                else if (o[i] instanceof Float)
                    sb.append(cs.internalsprintf(((Float) o[i]).floatValue()));
                else if (o[i] instanceof Double)
                    sb.append(cs.internalsprintf(((Double) o[i]).doubleValue()));
                else if (o[i] instanceof Character)
                    sb.append(cs.internalsprintf(((Character) o[i]).charValue()));
                else if (o[i] instanceof String)
                    sb.append(cs.internalsprintf((String) o[i]));
                else
                    sb.append(cs.internalsprintf(o[i]));
                if (!cs.isPositionalSpecification())
                    i++;
            }
        }
        return sb.toString();
    }

    /**
     * Format nothing. Just use the control string.
     * 
     * @return the formatted String.
     */
    public String sprintf() {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
        }
        return sb.toString();
    }

    /**
     * Format an int.
     * 
     * @param x The int to format.
     * @return The formatted String.
     * @exception IllegalArgumentException if the conversion character is f, e, E, g, G, s, or S.
     */
    public String sprintf(int x) throws IllegalArgumentException {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else
                sb.append(cs.internalsprintf(x));
        }
        return sb.toString();
    }

    /**
     * Format an long.
     * 
     * @param x The long to format.
     * @return The formatted String.
     * @exception IllegalArgumentException if the conversion character is f, e, E, g, G, s, or S.
     */
    public String sprintf(long x) throws IllegalArgumentException {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else
                sb.append(cs.internalsprintf(x));
        }
        return sb.toString();
    }

    /**
     * Format a double.
     * 
     * @param x The double to format.
     * @return The formatted String.
     * @exception IllegalArgumentException if the conversion character is c, C, s, S, d, d, x, X, or o.
     */
    public String sprintf(double x) throws IllegalArgumentException {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else
                sb.append(cs.internalsprintf(x));
        }
        return sb.toString();
    }

    /**
     * Format a String.
     * 
     * @param x The String to format.
     * @return The formatted String.
     * @exception IllegalArgumentException if the conversion character is neither s nor S.
     */
    public String sprintf(String x) throws IllegalArgumentException {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else
                sb.append(cs.internalsprintf(x));
        }
        return sb.toString();
    }

    /**
     * Format an Object. Convert wrapper types to their primitive equivalents and call the appropriate internal
     * formatting method. Convert Strings using an internal formatting method for Strings. Otherwise use the default
     * formatter (use toString).
     * 
     * @param x the Object to format.
     * @return the formatted String.
     * @exception IllegalArgumentException if the conversion character is inappropriate for formatting an unwrapped
     * value.
     */
    public String sprintf(Object x) throws IllegalArgumentException {
        Enumeration e = vFmt.elements();
        ConversionSpecification cs = null;
        char c = 0;
        StringBuffer sb = new StringBuffer();
        while (e.hasMoreElements()) {
            cs = (ConversionSpecification) e.nextElement();
            c = cs.getConversionCharacter();
            if (c == '\0')
                sb.append(cs.getLiteral());
            else if (c == '%')
                sb.append("%"); //$NON-NLS-1$
            else {
                if (x instanceof Byte)
                    sb.append(cs.internalsprintf(((Byte) x).byteValue()));
                else if (x instanceof Short)
                    sb.append(cs.internalsprintf(((Short) x).shortValue()));
                else if (x instanceof Integer)
                    sb.append(cs.internalsprintf(((Integer) x).intValue()));
                else if (x instanceof Long)
                    sb.append(cs.internalsprintf(((Long) x).longValue()));
                else if (x instanceof Float)
                    sb.append(cs.internalsprintf(((Float) x).floatValue()));
                else if (x instanceof Double)
                    sb.append(cs.internalsprintf(((Double) x).doubleValue()));
                else if (x instanceof Character)
                    sb.append(cs.internalsprintf(((Character) x).charValue()));
                else if (x instanceof String)
                    sb.append(cs.internalsprintf((String) x));
                else
                    sb.append(cs.internalsprintf(x));
            }
        }
        return sb.toString();
    }

    /**
     * <p>
     * ConversionSpecification allows the formatting of a single primitive or object embedded within a string. The
     * formatting is controlled by a format string. Only one Java primitive or object can be formatted at a time.
     * <p>
     * A format string is a Java string that contains a control string. The control string starts at the first percent
     * sign (%) in the string, provided that this percent sign
     * <ol>
     * <li>is not escaped protected by a matching % or is not an escape % character,
     * <li>is not at the end of the format string, and
     * <li>precedes a sequence of characters that parses as a valid control string.
     * </ol>
     * <p>
     * A control string takes the form:
     * 
     * <pre>
     *  % ['-+ #0]* [0..9]* { . [0..9]* }+
     *                 { [hlL] }+ [idfgGoxXeEcs]
     * </pre>
     * 
     * <p>
     * The behavior is like printf. One (hopefully the only) exception is that the minimum number of exponent digits is
     * 3 instead of 2 for e and E formats when the optional L is used before the e, E, g, or G conversion character. The
     * optional L does not imply conversion to a long long double.
     */
    private class ConversionSpecification {

        /**
         * Constructor. Used to prepare an instance to hold a literal, not a control string.
         */
        ConversionSpecification() {
        }

        /**
         * Constructor for a conversion specification. The argument must begin with a % and end with the conversion
         * character for the conversion specification.
         * 
         * @param fmtArg String specifying the conversion specification.
         * @exception IllegalArgumentException if the input string is null, zero length, or otherwise malformed.
         */
        ConversionSpecification(String fmtArg) throws IllegalArgumentException {
            if (fmtArg == null)
                throw new NullPointerException();
            if (fmtArg.length() == 0)
                throw new IllegalArgumentException("Control strings must have positive" + " lengths."); //$NON-NLS-1$ //$NON-NLS-2$
            if (fmtArg.charAt(0) == '%') {
                fmt = fmtArg;
                pos = 1;
                setArgPosition();
                setFlagCharacters();
                setFieldWidth();
                setPrecision();
                setOptionalHL();
                if (setConversionCharacter()) {
                    if (pos == fmtArg.length()) {
                        if (leadingZeros && leftJustify)
                            leadingZeros = false;
                        if (precisionSet && leadingZeros) {
                            if (conversionCharacter == 'd' || conversionCharacter == 'i' || conversionCharacter == 'o'
                                    || conversionCharacter == 'x') {
                                leadingZeros = false;
                            }
                        }
                    } else
                        throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); //$NON-NLS-1$
                } else
                    throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); //$NON-NLS-1$
            } else
                throw new IllegalArgumentException("Control strings must begin with %."); //$NON-NLS-1$
        }

        /**
         * Set the String for this instance.
         * 
         * @param s the String to store.
         */
        void setLiteral(String s) {
            fmt = s;
        }

        /**
         * Get the String for this instance. Translate any escape sequences.
         * 
         * @return s the stored String.
         */
        String getLiteral() {
            StringBuffer sb = new StringBuffer();
            int i = 0;
            while (i < fmt.length()) {
                if (fmt.charAt(i) == '\\') {
                    i++;
                    if (i < fmt.length()) {
                        char c = fmt.charAt(i);
                        switch (c) {
                        case 'a':
                            sb.append((char) 0x07);
                            break;
                        case 'b':
                            sb.append('\b');
                            break;
                        case 'f':
                            sb.append('\f');
                            break;
                        case 'n':
                            sb.append(System.getProperty("line.separator")); //$NON-NLS-1$
                            break;
                        case 'r':
                            sb.append('\r');
                            break;
                        case 't':
                            sb.append('\t');
                            break;
                        case 'v':
                            sb.append((char) 0x0b);
                            break;
                        case '\\':
                            sb.append('\\');
                            break;
                        }
                        i++;
                    } else
                        sb.append('\\');
                } else
                    i++;
            }
            return fmt;
        }

        /**
         * Get the conversion character that tells what type of control character this instance has.
         * 
         * @return the conversion character.
         */
        char getConversionCharacter() {
            return conversionCharacter;
        }

        /**
         * Check whether the specifier has a variable field width that is going to be set by an argument.
         * 
         * @return <code>true</code> if the conversion uses an * field width; otherwise <code>false</code>.
         */
        boolean isVariableFieldWidth() {
            return variableFieldWidth;
        }

        /**
         * Set the field width with an argument. A negative field width is taken as a - flag followed by a positive
         * field width.
         * 
         * @param fw the field width.
         */
        void setFieldWidthWithArg(int fw) {
            if (fw < 0)
                leftJustify = true;
            fieldWidthSet = true;
            fieldWidth = Math.abs(fw);
        }

        /**
         * Check whether the specifier has a variable precision that is going to be set by an argument.
         * 
         * @return <code>true</code> if the conversion uses an * precision; otherwise <code>false</code>.
         */
        boolean isVariablePrecision() {
            return variablePrecision;
        }

        /**
         * Set the precision with an argument. A negative precision will be changed to zero.
         * 
         * @param pr the precision.
         */
        void setPrecisionWithArg(int pr) {
            precisionSet = true;
            precision = Math.max(pr, 0);
        }

        /**
         * Format an int argument using this conversion specification.
         * 
         * @param s the int to format.
         * @return the formatted String.
         * @exception IllegalArgumentException if the conversion character is f, e, E, g, or G.
         */
        String internalsprintf(int s) throws IllegalArgumentException {
            String s2 = ""; //$NON-NLS-1$
            switch (conversionCharacter) {
            case 'd':
            case 'i':
                if (optionalh)
                    s2 = printDFormat((short) s);
                else if (optionall)
                    s2 = printDFormat((long) s);
                else
                    s2 = printDFormat(s);
                break;
            case 'x':
            case 'X':
                if (optionalh)
                    s2 = printXFormat((short) s);
                else if (optionall)
                    s2 = printXFormat((long) s);
                else
                    s2 = printXFormat(s);
                break;
            case 'o':
                if (optionalh)
                    s2 = printOFormat((short) s);
                else if (optionall)
                    s2 = printOFormat((long) s);
                else
                    s2 = printOFormat(s);
                break;
            case 'c':
            case 'C':
                s2 = printCFormat((char) s);
                break;
            default:
                throw new IllegalArgumentException("Cannot format a int with a format using a " + conversionCharacter //$NON-NLS-1$
                        + " conversion character."); //$NON-NLS-1$
            }
            return s2;
        }

        /**
         * Format a long argument using this conversion specification.
         * 
         * @param s the long to format.
         * @return the formatted String.
         * @exception IllegalArgumentException if the conversion character is f, e, E, g, or G.
         */
        String internalsprintf(long s) throws IllegalArgumentException {
            String s2 = ""; //$NON-NLS-1$
            switch (conversionCharacter) {
            case 'd':
            case 'i':
                if (optionalh)
                    s2 = printDFormat((short) s);
                else if (optionall)
                    s2 = printDFormat(s);
                else
                    s2 = printDFormat((int) s);
                break;
            case 'x':
            case 'X':
                if (optionalh)
                    s2 = printXFormat((short) s);
                else if (optionall)
                    s2 = printXFormat(s);
                else
                    s2 = printXFormat((int) s);
                break;
            case 'o':
                if (optionalh)
                    s2 = printOFormat((short) s);
                else if (optionall)
                    s2 = printOFormat(s);
                else
                    s2 = printOFormat((int) s);
                break;
            case 'c':
            case 'C':
                s2 = printCFormat((char) s);
                break;
            default:
                throw new IllegalArgumentException("Cannot format a long with a format using a " + conversionCharacter //$NON-NLS-1$
                        + " conversion character."); //$NON-NLS-1$
            }
            return s2;
        }

        /**
         * Format a double argument using this conversion specification.
         * 
         * @param s the double to format.
         * @return the formatted String.
         * @exception IllegalArgumentException if the conversion character is c, C, s, S, i, d, x, X, or o.
         */
        String internalsprintf(double s) throws IllegalArgumentException {
            String s2 = ""; //$NON-NLS-1$
            switch (conversionCharacter) {
            case 'f':
                s2 = printFFormat(s);
                break;
            case 'E':
            case 'e':
                s2 = printEFormat(s);
                break;
            case 'G':
            case 'g':
                s2 = printGFormat(s);
                break;
            default:
                throw new IllegalArgumentException("Cannot " + "format a double with a format using a " //$NON-NLS-1$ //$NON-NLS-2$
                        + conversionCharacter + " conversion character."); //$NON-NLS-1$
            }
            return s2;
        }

        /**
         * Format a String argument using this conversion specification.
         * 
         * @param s the String to format.
         * @return the formatted String.
         * @exception IllegalArgumentException if the conversion character is neither s nor S.
         */
        String internalsprintf(String s) throws IllegalArgumentException {
            String s2 = ""; //$NON-NLS-1$
            if (conversionCharacter == 's' || conversionCharacter == 'S')
                s2 = printSFormat(s);
            else
                throw new IllegalArgumentException("Cannot " + "format a String with a format using a " //$NON-NLS-1$ //$NON-NLS-2$
                        + conversionCharacter + " conversion character."); //$NON-NLS-1$
            return s2;
        }

        /**
         * Format an Object argument using this conversion specification.
         * 
         * @param s the Object to format.
         * @return the formatted String.
         * @exception IllegalArgumentException if the conversion character is neither s nor S.
         */
        String internalsprintf(Object s) {
            String s2 = ""; //$NON-NLS-1$
            if (conversionCharacter == 's' || conversionCharacter == 'S')
                s2 = printSFormat(s.toString());
            else
                throw new IllegalArgumentException("Cannot format a String with a format using" + " a " //$NON-NLS-1$ //$NON-NLS-2$
                        + conversionCharacter + " conversion character."); //$NON-NLS-1$
            return s2;
        }

        /**
         * For f format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the number of digits to appear after the radix character. Padding is with trailing
         * 0s.
         */
        private char[] fFormatDigits(double x) {
            // int defaultDigits=6;
            String sx, sxOut;
            int i, j, k;
            int n1In, n2In;
            int expon = 0;
            boolean minusSign = false;
            if (x > 0.0)
                sx = Double.toString(x);
            else if (x < 0.0) {
                sx = Double.toString(-x);
                minusSign = true;
            } else {
                sx = Double.toString(x);
                if (sx.charAt(0) == '-') {
                    minusSign = true;
                    sx = sx.substring(1);
                }
            }
            int ePos = sx.indexOf('E');
            int rPos = sx.indexOf('.');
            if (rPos != -1)
                n1In = rPos;
            else if (ePos != -1)
                n1In = ePos;
            else
                n1In = sx.length();
            if (rPos != -1) {
                if (ePos != -1)
                    n2In = ePos - rPos - 1;
                else
                    n2In = sx.length() - rPos - 1;
            } else
                n2In = 0;
            if (ePos != -1) {
                int ie = ePos + 1;
                expon = 0;
                if (sx.charAt(ie) == '-') {
                    for (++ie; ie < sx.length(); ie++)
                        if (sx.charAt(ie) != '0')
                            break;
                    if (ie < sx.length())
                        expon = -Integer.parseInt(sx.substring(ie));
                } else {
                    if (sx.charAt(ie) == '+')
                        ++ie;
                    for (; ie < sx.length(); ie++)
                        if (sx.charAt(ie) != '0')
                            break;
                    if (ie < sx.length())
                        expon = Integer.parseInt(sx.substring(ie));
                }
            }
            int p;
            if (precisionSet)
                p = precision;
            else
                p = defaultDigits - 1;
            char[] ca1 = sx.toCharArray();
            char[] ca2 = new char[n1In + n2In];
            char[] ca3, ca4, ca5;
            for (j = 0; j < n1In; j++)
                ca2[j] = ca1[j];
            i = j + 1;
            for (k = 0; k < n2In; j++, i++, k++)
                ca2[j] = ca1[i];
            if (n1In + expon <= 0) {
                ca3 = new char[-expon + n2In];
                for (j = 0, k = 0; k < (-n1In - expon); k++, j++)
                    ca3[j] = '0';
                for (i = 0; i < (n1In + n2In); i++, j++)
                    ca3[j] = ca2[i];
            } else
                ca3 = ca2;
            boolean carry = false;
            if (p < -expon + n2In) {
                if (expon < 0)
                    i = p;
                else
                    i = p + n1In;
                carry = checkForCarry(ca3, i);
                if (carry)
                    carry = startSymbolicCarry(ca3, i - 1, 0);
            }
            if (n1In + expon <= 0) {
                ca4 = new char[2 + p];
                if (!carry)
                    ca4[0] = '0';
                else
                    ca4[0] = '1';
                if (alternateForm || !precisionSet || precision != 0) {
                    ca4[1] = '.';
                    for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++)
                        ca4[j] = ca3[i];
                    for (; j < ca4.length; j++)
                        ca4[j] = '0';
                }
            } else {
                if (!carry) {
                    if (alternateForm || !precisionSet || precision != 0)
                        ca4 = new char[n1In + expon + p + 1];
                    else
                        ca4 = new char[n1In + expon];
                    j = 0;
                } else {
                    if (alternateForm || !precisionSet || precision != 0)
                        ca4 = new char[n1In + expon + p + 2];
                    else
                        ca4 = new char[n1In + expon + 1];
                    ca4[0] = '1';
                    j = 1;
                }
                for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++)
                    ca4[j] = ca3[i];
                for (; i < n1In + expon; i++, j++)
                    ca4[j] = '0';
                if (alternateForm || !precisionSet || precision != 0) {
                    ca4[j] = '.';
                    j++;
                    for (k = 0; i < ca3.length && k < p; i++, j++, k++)
                        ca4[j] = ca3[i];
                    for (; j < ca4.length; j++)
                        ca4[j] = '0';
                }
            }
            int nZeros = 0;
            if (!leftJustify && leadingZeros) {
                int xThousands = 0;
                if (thousands) {
                    int xlead = 0;
                    if (ca4[0] == '+' || ca4[0] == '-' || ca4[0] == ' ')
                        xlead = 1;
                    int xdp = xlead;
                    for (; xdp < ca4.length; xdp++)
                        if (ca4[xdp] == '.')
                            break;
                    xThousands = (xdp - xlead) / 3;
                }
                if (fieldWidthSet)
                    nZeros = fieldWidth - ca4.length;
                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
                    nZeros--;
                nZeros -= xThousands;
                if (nZeros < 0)
                    nZeros = 0;
            }
            j = 0;
            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) {
                ca5 = new char[ca4.length + nZeros + 1];
                j++;
            } else
                ca5 = new char[ca4.length + nZeros];
            if (!minusSign) {
                if (leadingSign)
                    ca5[0] = '+';
                if (leadingSpace)
                    ca5[0] = ' ';
            } else
                ca5[0] = '-';
            for (i = 0; i < nZeros; i++, j++)
                ca5[j] = '0';
            for (i = 0; i < ca4.length; i++, j++)
                ca5[j] = ca4[i];

            int lead = 0;
            if (ca5[0] == '+' || ca5[0] == '-' || ca5[0] == ' ')
                lead = 1;
            int dp = lead;
            for (; dp < ca5.length; dp++)
                if (ca5[dp] == '.')
                    break;
            int nThousands = (dp - lead) / 3;
            // Localize the decimal point.
            if (dp < ca5.length)
                ca5[dp] = dfs.getDecimalSeparator();
            char[] ca6 = ca5;
            if (thousands && nThousands > 0) {
                ca6 = new char[ca5.length + nThousands + lead];
                ca6[0] = ca5[0];
                for (i = lead, k = lead; i < dp; i++) {
                    if (i > 0 && (dp - i) % 3 == 0) {
                        // ca6[k]=',';
                        ca6[k] = dfs.getGroupingSeparator();
                        ca6[k + 1] = ca5[i];
                        k += 2;
                    } else {
                        ca6[k] = ca5[i];
                        k++;
                    }
                }
                for (; i < ca5.length; i++, k++) {
                    ca6[k] = ca5[i];
                }
            }
            return ca6;
        }

        /**
         * An intermediate routine on the way to creating an f format String. The method decides whether the input
         * double value is an infinity, not-a-number, or a finite double and formats each type of input appropriately.
         * 
         * @param x the double value to be formatted.
         * @return the converted double value.
         */
        private String fFormatString(double x) {
            boolean noDigits = false;
            char[] ca6, ca7;
            if (Double.isInfinite(x)) {
                if (x == Double.POSITIVE_INFINITY) {
                    if (leadingSign)
                        ca6 = "+Inf".toCharArray(); //$NON-NLS-1$
                    else if (leadingSpace)
                        ca6 = " Inf".toCharArray(); //$NON-NLS-1$
                    else
                        ca6 = "Inf".toCharArray(); //$NON-NLS-1$
                } else
                    ca6 = "-Inf".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else if (Double.isNaN(x)) {
                if (leadingSign)
                    ca6 = "+NaN".toCharArray(); //$NON-NLS-1$
                else if (leadingSpace)
                    ca6 = " NaN".toCharArray(); //$NON-NLS-1$
                else
                    ca6 = "NaN".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else
                ca6 = fFormatDigits(x);
            ca7 = applyFloatPadding(ca6, false);
            return new String(ca7);
        }

        /**
         * For e format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear after the radix character. Padding is with
         * trailing 0s.
         * 
         * The behavior is like printf. One (hopefully the only) exception is that the minimum number of exponent digits
         * is 3 instead of 2 for e and E formats when the optional L is used before the e, E, g, or G conversion
         * character. The optional L does not imply conversion to a long long double.
         */
        private char[] eFormatDigits(double x, char eChar) {
            char[] ca1, ca2, ca3;
            // int defaultDigits=6;
            String sx, sxOut;
            int i, j, k, p;
            int n1In, n2In;
            int expon = 0;
            int ePos, rPos, eSize;
            boolean minusSign = false;
            if (x > 0.0)
                sx = Double.toString(x);
            else if (x < 0.0) {
                sx = Double.toString(-x);
                minusSign = true;
            } else {
                sx = Double.toString(x);
                if (sx.charAt(0) == '-') {
                    minusSign = true;
                    sx = sx.substring(1);
                }
            }
            ePos = sx.indexOf('E');
            if (ePos == -1)
                ePos = sx.indexOf('e');
            rPos = sx.indexOf('.');
            if (rPos != -1)
                n1In = rPos;
            else if (ePos != -1)
                n1In = ePos;
            else
                n1In = sx.length();
            if (rPos != -1) {
                if (ePos != -1)
                    n2In = ePos - rPos - 1;
                else
                    n2In = sx.length() - rPos - 1;
            } else
                n2In = 0;
            if (ePos != -1) {
                int ie = ePos + 1;
                expon = 0;
                if (sx.charAt(ie) == '-') {
                    for (++ie; ie < sx.length(); ie++)
                        if (sx.charAt(ie) != '0')
                            break;
                    if (ie < sx.length())
                        expon = -Integer.parseInt(sx.substring(ie));
                } else {
                    if (sx.charAt(ie) == '+')
                        ++ie;
                    for (; ie < sx.length(); ie++)
                        if (sx.charAt(ie) != '0')
                            break;
                    if (ie < sx.length())
                        expon = Integer.parseInt(sx.substring(ie));
                }
            }
            if (rPos != -1)
                expon += rPos - 1;
            if (precisionSet)
                p = precision;
            else
                p = defaultDigits - 1;
            if (rPos != -1 && ePos != -1)
                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
            else if (rPos != -1)
                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
            else if (ePos != -1)
                ca1 = sx.substring(0, ePos).toCharArray();
            else
                ca1 = sx.toCharArray();
            boolean carry = false;
            int i0 = 0;
            if (ca1[0] != '0')
                i0 = 0;
            else
                for (i0 = 0; i0 < ca1.length; i0++)
                    if (ca1[i0] != '0')
                        break;
            if (i0 + p < ca1.length - 1) {
                carry = checkForCarry(ca1, i0 + p + 1);
                if (carry)
                    carry = startSymbolicCarry(ca1, i0 + p, i0);
                if (carry) {
                    ca2 = new char[i0 + p + 1];
                    ca2[i0] = '1';
                    for (j = 0; j < i0; j++)
                        ca2[j] = '0';
                    for (i = i0, j = i0 + 1; j < p + 1; i++, j++)
                        ca2[j] = ca1[i];
                    expon++;
                    ca1 = ca2;
                }
            }
            if (Math.abs(expon) < 100 && !optionalL)
                eSize = 4;
            else
                eSize = 5;
            if (alternateForm || !precisionSet || precision != 0)
                ca2 = new char[2 + p + eSize];
            else
                ca2 = new char[1 + eSize];
            if (ca1[0] != '0') {
                ca2[0] = ca1[0];
                j = 1;
            } else {
                for (j = 1; j < (ePos == -1 ? ca1.length : ePos); j++)
                    if (ca1[j] != '0')
                        break;
                if ((ePos != -1 && j < ePos) || (ePos == -1 && j < ca1.length)) {
                    ca2[0] = ca1[j];
                    expon -= j;
                    j++;
                } else {
                    ca2[0] = '0';
                    j = 2;
                }
            }
            if (alternateForm || !precisionSet || precision != 0) {
                ca2[1] = '.';
                i = 2;
            } else
                i = 1;
            for (k = 0; k < p && j < ca1.length; j++, i++, k++)
                ca2[i] = ca1[j];
            for (; i < ca2.length - eSize; i++)
                ca2[i] = '0';
            ca2[i++] = eChar;
            if (expon < 0)
                ca2[i++] = '-';
            else
                ca2[i++] = '+';
            expon = Math.abs(expon);
            if (expon >= 100) {
                switch (expon / 100) {
                case 1:
                    ca2[i] = '1';
                    break;
                case 2:
                    ca2[i] = '2';
                    break;
                case 3:
                    ca2[i] = '3';
                    break;
                case 4:
                    ca2[i] = '4';
                    break;
                case 5:
                    ca2[i] = '5';
                    break;
                case 6:
                    ca2[i] = '6';
                    break;
                case 7:
                    ca2[i] = '7';
                    break;
                case 8:
                    ca2[i] = '8';
                    break;
                case 9:
                    ca2[i] = '9';
                    break;
                }
                i++;
            }
            switch ((expon % 100) / 10) {
            case 0:
                ca2[i] = '0';
                break;
            case 1:
                ca2[i] = '1';
                break;
            case 2:
                ca2[i] = '2';
                break;
            case 3:
                ca2[i] = '3';
                break;
            case 4:
                ca2[i] = '4';
                break;
            case 5:
                ca2[i] = '5';
                break;
            case 6:
                ca2[i] = '6';
                break;
            case 7:
                ca2[i] = '7';
                break;
            case 8:
                ca2[i] = '8';
                break;
            case 9:
                ca2[i] = '9';
                break;
            }
            i++;
            switch (expon % 10) {
            case 0:
                ca2[i] = '0';
                break;
            case 1:
                ca2[i] = '1';
                break;
            case 2:
                ca2[i] = '2';
                break;
            case 3:
                ca2[i] = '3';
                break;
            case 4:
                ca2[i] = '4';
                break;
            case 5:
                ca2[i] = '5';
                break;
            case 6:
                ca2[i] = '6';
                break;
            case 7:
                ca2[i] = '7';
                break;
            case 8:
                ca2[i] = '8';
                break;
            case 9:
                ca2[i] = '9';
                break;
            }
            int nZeros = 0;
            if (!leftJustify && leadingZeros) {
                int xThousands = 0;
                if (thousands) {
                    int xlead = 0;
                    if (ca2[0] == '+' || ca2[0] == '-' || ca2[0] == ' ')
                        xlead = 1;
                    int xdp = xlead;
                    for (; xdp < ca2.length; xdp++)
                        if (ca2[xdp] == '.')
                            break;
                    xThousands = (xdp - xlead) / 3;
                }
                if (fieldWidthSet)
                    nZeros = fieldWidth - ca2.length;
                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
                    nZeros--;
                nZeros -= xThousands;
                if (nZeros < 0)
                    nZeros = 0;
            }
            j = 0;
            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) {
                ca3 = new char[ca2.length + nZeros + 1];
                j++;
            } else
                ca3 = new char[ca2.length + nZeros];
            if (!minusSign) {
                if (leadingSign)
                    ca3[0] = '+';
                if (leadingSpace)
                    ca3[0] = ' ';
            } else
                ca3[0] = '-';
            for (k = 0; k < nZeros; j++, k++)
                ca3[j] = '0';
            for (i = 0; i < ca2.length && j < ca3.length; i++, j++)
                ca3[j] = ca2[i];

            int lead = 0;
            if (ca3[0] == '+' || ca3[0] == '-' || ca3[0] == ' ')
                lead = 1;
            int dp = lead;
            for (; dp < ca3.length; dp++)
                if (ca3[dp] == '.')
                    break;
            int nThousands = dp / 3;
            // Localize the decimal point.
            if (dp < ca3.length)
                ca3[dp] = dfs.getDecimalSeparator();
            char[] ca4 = ca3;
            if (thousands && nThousands > 0) {
                ca4 = new char[ca3.length + nThousands + lead];
                ca4[0] = ca3[0];
                for (i = lead, k = lead; i < dp; i++) {
                    if (i > 0 && (dp - i) % 3 == 0) {
                        // ca4[k]=',';
                        ca4[k] = dfs.getGroupingSeparator();
                        ca4[k + 1] = ca3[i];
                        k += 2;
                    } else {
                        ca4[k] = ca3[i];
                        k++;
                    }
                }
                for (; i < ca3.length; i++, k++)
                    ca4[k] = ca3[i];
            }
            return ca4;
        }

        /**
         * Check to see if the digits that are going to be truncated because of the precision should force a round in
         * the preceding digits.
         * 
         * @param ca1 the array of digits
         * @param icarry the index of the first digit that is to be truncated from the print
         * @return <code>true</code> if the truncation forces a round that will change the print
         */
        private boolean checkForCarry(char[] ca1, int icarry) {
            boolean carry = false;
            if (icarry < ca1.length) {
                if (ca1[icarry] == '6' || ca1[icarry] == '7' || ca1[icarry] == '8' || ca1[icarry] == '9')
                    carry = true;
                else if (ca1[icarry] == '5') {
                    int ii = icarry + 1;
                    for (; ii < ca1.length; ii++)
                        if (ca1[ii] != '0')
                            break;
                    carry = ii < ca1.length;
                    if (!carry && icarry > 0) {
                        carry = (ca1[icarry - 1] == '1' || ca1[icarry - 1] == '3' || ca1[icarry - 1] == '5'
                                || ca1[icarry - 1] == '7' || ca1[icarry - 1] == '9');
                    }
                }
            }
            return carry;
        }

        /**
         * Start the symbolic carry process. The process is not quite finished because the symbolic carry may change the
         * length of the string and change the exponent (in e format).
         * 
         * @param cLast index of the last digit changed by the round
         * @param cFirst index of the first digit allowed to be changed by this phase of the round
         * @return <code>true</code> if the carry forces a round that will change the print still more
         */
        private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst) {
            boolean carry = true;
            for (int i = cLast; carry && i >= cFirst; i--) {
                carry = false;
                switch (ca[i]) {
                case '0':
                    ca[i] = '1';
                    break;
                case '1':
                    ca[i] = '2';
                    break;
                case '2':
                    ca[i] = '3';
                    break;
                case '3':
                    ca[i] = '4';
                    break;
                case '4':
                    ca[i] = '5';
                    break;
                case '5':
                    ca[i] = '6';
                    break;
                case '6':
                    ca[i] = '7';
                    break;
                case '7':
                    ca[i] = '8';
                    break;
                case '8':
                    ca[i] = '9';
                    break;
                case '9':
                    ca[i] = '0';
                    carry = true;
                    break;
                }
            }
            return carry;
        }

        /**
         * An intermediate routine on the way to creating an e format String. The method decides whether the input
         * double value is an infinity, not-a-number, or a finite double and formats each type of input appropriately.
         * 
         * @param x the double value to be formatted.
         * @param eChar an 'e' or 'E' to use in the converted double value.
         * @return the converted double value.
         */
        private String eFormatString(double x, char eChar) {
            boolean noDigits = false;
            char[] ca4, ca5;
            if (Double.isInfinite(x)) {
                if (x == Double.POSITIVE_INFINITY) {
                    if (leadingSign)
                        ca4 = "+Inf".toCharArray(); //$NON-NLS-1$
                    else if (leadingSpace)
                        ca4 = " Inf".toCharArray(); //$NON-NLS-1$
                    else
                        ca4 = "Inf".toCharArray(); //$NON-NLS-1$
                } else
                    ca4 = "-Inf".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else if (Double.isNaN(x)) {
                if (leadingSign)
                    ca4 = "+NaN".toCharArray(); //$NON-NLS-1$
                else if (leadingSpace)
                    ca4 = " NaN".toCharArray(); //$NON-NLS-1$
                else
                    ca4 = "NaN".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else
                ca4 = eFormatDigits(x, eChar);
            ca5 = applyFloatPadding(ca4, false);
            return new String(ca5);
        }

        /**
         * Apply zero or blank, left or right padding.
         * 
         * @param ca4 array of characters before padding is finished
         * @param noDigits NaN or signed Inf
         * @return a padded array of characters
         */
        private char[] applyFloatPadding(char[] ca4, boolean noDigits) {
            char[] ca5 = ca4;
            if (fieldWidthSet) {
                int i, j, nBlanks;
                if (leftJustify) {
                    nBlanks = fieldWidth - ca4.length;
                    if (nBlanks > 0) {
                        ca5 = new char[ca4.length + nBlanks];
                        for (i = 0; i < ca4.length; i++)
                            ca5[i] = ca4[i];
                        for (j = 0; j < nBlanks; j++, i++)
                            ca5[i] = ' ';
                    }
                } else if (!leadingZeros || noDigits) {
                    nBlanks = fieldWidth - ca4.length;
                    if (nBlanks > 0) {
                        ca5 = new char[ca4.length + nBlanks];
                        for (i = 0; i < nBlanks; i++)
                            ca5[i] = ' ';
                        for (j = 0; j < ca4.length; i++, j++)
                            ca5[i] = ca4[j];
                    }
                } else if (leadingZeros) {
                    nBlanks = fieldWidth - ca4.length;
                    if (nBlanks > 0) {
                        ca5 = new char[ca4.length + nBlanks];
                        i = 0;
                        j = 0;
                        if (ca4[0] == '-') {
                            ca5[0] = '-';
                            i++;
                            j++;
                        }
                        for (int k = 0; k < nBlanks; i++, k++)
                            ca5[i] = '0';
                        for (; j < ca4.length; i++, j++)
                            ca5[i] = ca4[j];
                    }
                }
            }
            return ca5;
        }

        /**
         * Format method for the f conversion character.
         * 
         * @param x the double to format.
         * @return the formatted String.
         */
        private String printFFormat(double x) {
            return fFormatString(x);
        }

        /**
         * Format method for the e or E conversion character.
         * 
         * @param x the double to format.
         * @return the formatted String.
         */
        private String printEFormat(double x) {
            if (conversionCharacter == 'e')
                return eFormatString(x, 'e');
            else
                return eFormatString(x, 'E');
        }

        /**
         * Format method for the g conversion character.
         * 
         * For g format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear after the radix character. Padding is with
         * trailing 0s.
         * 
         * @param x the double to format.
         * @return the formatted String.
         */
        private String printGFormat(double x) {
            String sx, sy, sz, ret;
            int savePrecision = precision;
            int i;
            char[] ca4, ca5;
            boolean noDigits = false;
            if (Double.isInfinite(x)) {
                if (x == Double.POSITIVE_INFINITY) {
                    if (leadingSign)
                        ca4 = "+Inf".toCharArray(); //$NON-NLS-1$
                    else if (leadingSpace)
                        ca4 = " Inf".toCharArray(); //$NON-NLS-1$
                    else
                        ca4 = "Inf".toCharArray(); //$NON-NLS-1$
                } else
                    ca4 = "-Inf".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else if (Double.isNaN(x)) {
                if (leadingSign)
                    ca4 = "+NaN".toCharArray(); //$NON-NLS-1$
                else if (leadingSpace)
                    ca4 = " NaN".toCharArray(); //$NON-NLS-1$
                else
                    ca4 = "NaN".toCharArray(); //$NON-NLS-1$
                noDigits = true;
            } else {
                if (!precisionSet)
                    precision = defaultDigits;
                if (precision == 0)
                    precision = 1;
                int ePos = -1;
                if (conversionCharacter == 'g') {
                    sx = eFormatString(x, 'e').trim();
                    ePos = sx.indexOf('e');
                } else {
                    sx = eFormatString(x, 'E').trim();
                    ePos = sx.indexOf('E');
                }
                i = ePos + 1;
                int expon = 0;
                if (sx.charAt(i) == '-') {
                    for (++i; i < sx.length(); i++)
                        if (sx.charAt(i) != '0')
                            break;
                    if (i < sx.length())
                        expon = -Integer.parseInt(sx.substring(i));
                } else {
                    if (sx.charAt(i) == '+')
                        ++i;
                    for (; i < sx.length(); i++)
                        if (sx.charAt(i) != '0')
                            break;
                    if (i < sx.length())
                        expon = Integer.parseInt(sx.substring(i));
                }
                // Trim trailing zeros.
                // If the radix character is not followed by
                // a digit, trim it, too.
                if (!alternateForm) {
                    if (expon >= -4 && expon < precision)
                        sy = fFormatString(x).trim();
                    else
                        sy = sx.substring(0, ePos);
                    i = sy.length() - 1;
                    for (; i >= 0; i--)
                        if (sy.charAt(i) != '0')
                            break;
                    if (i >= 0 && sy.charAt(i) == '.')
                        i--;
                    if (i == -1)
                        sz = "0"; //$NON-NLS-1$
                    else if (!Character.isDigit(sy.charAt(i)))
                        sz = sy.substring(0, i + 1) + "0"; //$NON-NLS-1$
                    else
                        sz = sy.substring(0, i + 1);
                    if (expon >= -4 && expon < precision)
                        ret = sz;
                    else
                        ret = sz + sx.substring(ePos);
                } else {
                    if (expon >= -4 && expon < precision)
                        ret = fFormatString(x).trim();
                    else
                        ret = sx;
                }
                // leading space was trimmed off during
                // construction
                if (leadingSpace)
                    if (x >= 0)
                        ret = " " + ret; //$NON-NLS-1$
                ca4 = ret.toCharArray();
            }
            // Pad with blanks or zeros.
            ca5 = applyFloatPadding(ca4, false);
            precision = savePrecision;
            return new String(ca5);
        }

        /**
         * Format method for the d conversion specifer and short argument.
         * 
         * For d format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. A '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the short to format.
         * @return the formatted String.
         */
        private String printDFormat(short x) {
            return printDFormat(Short.toString(x));
        }

        /**
         * Format method for the d conversion character and long argument.
         * 
         * For d format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. A '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the long to format.
         * @return the formatted String.
         */
        private String printDFormat(long x) {
            return printDFormat(Long.toString(x));
        }

        /**
         * Format method for the d conversion character and int argument.
         * 
         * For d format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. A '+' character means that the conversion will always begin with a
         * sign (+ or -). The blank flag character means that a non-negative input will be preceded with a blank. If
         * both a '+' and a ' ' are specified, the blank flag is ignored. The '0' flag character implies that padding to
         * the field width will be done with zeros instead of blanks.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the int to format.
         * @return the formatted String.
         */
        private String printDFormat(int x) {
            return printDFormat(Integer.toString(x));
        }

        /**
         * Utility method for formatting using the d conversion character.
         * 
         * @param sx the String to format, the result of converting a short, int, or long to a String.
         * @return the formatted String.
         */
        private String printDFormat(String sx) {
            int nLeadingZeros = 0;
            int nBlanks = 0, n = 0;
            int i = 0, jFirst = 0;
            boolean neg = sx.charAt(0) == '-';
            if (sx.equals("0") && precisionSet && precision == 0) //$NON-NLS-1$
                sx = ""; //$NON-NLS-1$
            if (!neg) {
                if (precisionSet && sx.length() < precision)
                    nLeadingZeros = precision - sx.length();
            } else {
                if (precisionSet && (sx.length() - 1) < precision)
                    nLeadingZeros = precision - sx.length() + 1;
            }
            if (nLeadingZeros < 0)
                nLeadingZeros = 0;
            if (fieldWidthSet) {
                nBlanks = fieldWidth - nLeadingZeros - sx.length();
                if (!neg && (leadingSign || leadingSpace))
                    nBlanks--;
            }
            if (nBlanks < 0)
                nBlanks = 0;
            if (leadingSign)
                n++;
            else if (leadingSpace)
                n++;
            n += nBlanks;
            n += nLeadingZeros;
            n += sx.length();
            char[] ca = new char[n];
            if (leftJustify) {
                if (neg)
                    ca[i++] = '-';
                else if (leadingSign)
                    ca[i++] = '+';
                else if (leadingSpace)
                    ca[i++] = ' ';
                char[] csx = sx.toCharArray();
                jFirst = neg ? 1 : 0;
                for (int j = 0; j < nLeadingZeros; i++, j++)
                    ca[i] = '0';
                for (int j = jFirst; j < csx.length; j++, i++)
                    ca[i] = csx[j];
                for (int j = 0; j < nBlanks; i++, j++)
                    ca[i] = ' ';
            } else {
                if (!leadingZeros) {
                    for (i = 0; i < nBlanks; i++)
                        ca[i] = ' ';
                    if (neg)
                        ca[i++] = '-';
                    else if (leadingSign)
                        ca[i++] = '+';
                    else if (leadingSpace)
                        ca[i++] = ' ';
                } else {
                    if (neg)
                        ca[i++] = '-';
                    else if (leadingSign)
                        ca[i++] = '+';
                    else if (leadingSpace)
                        ca[i++] = ' ';
                    for (int j = 0; j < nBlanks; j++, i++)
                        ca[i] = '0';
                }
                for (int j = 0; j < nLeadingZeros; j++, i++)
                    ca[i] = '0';
                char[] csx = sx.toCharArray();
                jFirst = neg ? 1 : 0;
                for (int j = jFirst; j < csx.length; j++, i++)
                    ca[i] = csx[j];
            }
            return new String(ca);
        }

        /**
         * Format method for the x conversion character and short argument.
         * 
         * For x format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means to lead with '0x'.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the short to format.
         * @return the formatted String.
         */
        private String printXFormat(short x) {
            String sx = null;
            if (x == Short.MIN_VALUE)
                sx = "8000"; //$NON-NLS-1$
            else if (x < 0) {
                String t;
                if (x == Short.MIN_VALUE)
                    t = "0"; //$NON-NLS-1$
                else {
                    t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
                    if (t.charAt(0) == 'F' || t.charAt(0) == 'f')
                        t = t.substring(16, 32);
                }
                switch (t.length()) {
                case 1:
                    sx = "800" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "80" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "8" + t; //$NON-NLS-1$
                    break;
                case 4:
                    switch (t.charAt(0)) {
                    case '1':
                        sx = "9" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '2':
                        sx = "a" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '3':
                        sx = "b" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '4':
                        sx = "c" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '5':
                        sx = "d" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '6':
                        sx = "e" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    case '7':
                        sx = "f" + t.substring(1, 4); //$NON-NLS-1$
                        break;
                    }
                    break;
                }
            } else
                sx = Integer.toString((int) x, 16);
            return printXFormat(sx);
        }

        /**
         * Format method for the x conversion character and long argument.
         * 
         * For x format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means to lead with '0x'.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the long to format.
         * @return the formatted String.
         */
        private String printXFormat(long x) {
            String sx = null;
            if (x == Long.MIN_VALUE)
                sx = "8000000000000000"; //$NON-NLS-1$
            else if (x < 0) {
                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
                switch (t.length()) {
                case 1:
                    sx = "800000000000000" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "80000000000000" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "8000000000000" + t; //$NON-NLS-1$
                    break;
                case 4:
                    sx = "800000000000" + t; //$NON-NLS-1$
                    break;
                case 5:
                    sx = "80000000000" + t; //$NON-NLS-1$
                    break;
                case 6:
                    sx = "8000000000" + t; //$NON-NLS-1$
                    break;
                case 7:
                    sx = "800000000" + t; //$NON-NLS-1$
                    break;
                case 8:
                    sx = "80000000" + t; //$NON-NLS-1$
                    break;
                case 9:
                    sx = "8000000" + t; //$NON-NLS-1$
                    break;
                case 10:
                    sx = "800000" + t; //$NON-NLS-1$
                    break;
                case 11:
                    sx = "80000" + t; //$NON-NLS-1$
                    break;
                case 12:
                    sx = "8000" + t; //$NON-NLS-1$
                    break;
                case 13:
                    sx = "800" + t; //$NON-NLS-1$
                    break;
                case 14:
                    sx = "80" + t; //$NON-NLS-1$
                    break;
                case 15:
                    sx = "8" + t; //$NON-NLS-1$
                    break;
                case 16:
                    switch (t.charAt(0)) {
                    case '1':
                        sx = "9" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '2':
                        sx = "a" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '3':
                        sx = "b" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '4':
                        sx = "c" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '5':
                        sx = "d" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '6':
                        sx = "e" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    case '7':
                        sx = "f" + t.substring(1, 16); //$NON-NLS-1$
                        break;
                    }
                    break;
                }
            } else
                sx = Long.toString(x, 16);
            return printXFormat(sx);
        }

        /**
         * Format method for the x conversion character and int argument.
         * 
         * For x format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means to lead with '0x'.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the int to format.
         * @return the formatted String.
         */
        private String printXFormat(int x) {
            String sx = null;
            if (x == Integer.MIN_VALUE)
                sx = "80000000"; //$NON-NLS-1$
            else if (x < 0) {
                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
                switch (t.length()) {
                case 1:
                    sx = "8000000" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "800000" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "80000" + t; //$NON-NLS-1$
                    break;
                case 4:
                    sx = "8000" + t; //$NON-NLS-1$
                    break;
                case 5:
                    sx = "800" + t; //$NON-NLS-1$
                    break;
                case 6:
                    sx = "80" + t; //$NON-NLS-1$
                    break;
                case 7:
                    sx = "8" + t; //$NON-NLS-1$
                    break;
                case 8:
                    switch (t.charAt(0)) {
                    case '1':
                        sx = "9" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '2':
                        sx = "a" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '3':
                        sx = "b" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '4':
                        sx = "c" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '5':
                        sx = "d" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '6':
                        sx = "e" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    case '7':
                        sx = "f" + t.substring(1, 8); //$NON-NLS-1$
                        break;
                    }
                    break;
                }
            } else
                sx = Integer.toString(x, 16);
            return printXFormat(sx);
        }

        /**
         * Utility method for formatting using the x conversion character.
         * 
         * @param sx the String to format, the result of converting a short, int, or long to a String.
         * @return the formatted String.
         */
        private String printXFormat(String sx) {
            int nLeadingZeros = 0;
            int nBlanks = 0;
            if (sx.equals("0") && precisionSet && precision == 0) //$NON-NLS-1$
                sx = ""; //$NON-NLS-1$
            if (precisionSet)
                nLeadingZeros = precision - sx.length();
            if (nLeadingZeros < 0)
                nLeadingZeros = 0;
            if (fieldWidthSet) {
                nBlanks = fieldWidth - nLeadingZeros - sx.length();
                if (alternateForm)
                    nBlanks = nBlanks - 2;
            }
            if (nBlanks < 0)
                nBlanks = 0;
            int n = 0;
            if (alternateForm)
                n += 2;
            n += nLeadingZeros;
            n += sx.length();
            n += nBlanks;
            char[] ca = new char[n];
            int i = 0;
            if (leftJustify) {
                if (alternateForm) {
                    ca[i++] = '0';
                    ca[i++] = 'x';
                }
                for (int j = 0; j < nLeadingZeros; j++, i++)
                    ca[i] = '0';
                char[] csx = sx.toCharArray();
                for (int j = 0; j < csx.length; j++, i++)
                    ca[i] = csx[j];
                for (int j = 0; j < nBlanks; j++, i++)
                    ca[i] = ' ';
            } else {
                if (!leadingZeros)
                    for (int j = 0; j < nBlanks; j++, i++)
                        ca[i] = ' ';
                if (alternateForm) {
                    ca[i++] = '0';
                    ca[i++] = 'x';
                }
                if (leadingZeros)
                    for (int j = 0; j < nBlanks; j++, i++)
                        ca[i] = '0';
                for (int j = 0; j < nLeadingZeros; j++, i++)
                    ca[i] = '0';
                char[] csx = sx.toCharArray();
                for (int j = 0; j < csx.length; j++, i++)
                    ca[i] = csx[j];
            }
            String caReturn = new String(ca);
            if (conversionCharacter == 'X')
                caReturn = caReturn.toUpperCase();
            return caReturn;
        }

        /**
         * Format method for the o conversion character and short argument.
         * 
         * For o format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means that the output begins with a leading
         * 0 and the precision is increased by 1.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the short to format.
         * @return the formatted String.
         */
        private String printOFormat(short x) {
            String sx = null;
            if (x == Short.MIN_VALUE)
                sx = "100000"; //$NON-NLS-1$
            else if (x < 0) {
                String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
                switch (t.length()) {
                case 1:
                    sx = "10000" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "1000" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "100" + t; //$NON-NLS-1$
                    break;
                case 4:
                    sx = "10" + t; //$NON-NLS-1$
                    break;
                case 5:
                    sx = "1" + t; //$NON-NLS-1$
                    break;
                }
            } else
                sx = Integer.toString((int) x, 8);
            return printOFormat(sx);
        }

        /**
         * Format method for the o conversion character and long argument.
         * 
         * For o format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means that the output begins with a leading
         * 0 and the precision is increased by 1.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the long to format.
         * @return the formatted String.
         */
        private String printOFormat(long x) {
            String sx = null;
            if (x == Long.MIN_VALUE)
                sx = "1000000000000000000000"; //$NON-NLS-1$
            else if (x < 0) {
                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
                switch (t.length()) {
                case 1:
                    sx = "100000000000000000000" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "10000000000000000000" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "1000000000000000000" + t; //$NON-NLS-1$
                    break;
                case 4:
                    sx = "100000000000000000" + t; //$NON-NLS-1$
                    break;
                case 5:
                    sx = "10000000000000000" + t; //$NON-NLS-1$
                    break;
                case 6:
                    sx = "1000000000000000" + t; //$NON-NLS-1$
                    break;
                case 7:
                    sx = "100000000000000" + t; //$NON-NLS-1$
                    break;
                case 8:
                    sx = "10000000000000" + t; //$NON-NLS-1$
                    break;
                case 9:
                    sx = "1000000000000" + t; //$NON-NLS-1$
                    break;
                case 10:
                    sx = "100000000000" + t; //$NON-NLS-1$
                    break;
                case 11:
                    sx = "10000000000" + t; //$NON-NLS-1$
                    break;
                case 12:
                    sx = "1000000000" + t; //$NON-NLS-1$
                    break;
                case 13:
                    sx = "100000000" + t; //$NON-NLS-1$
                    break;
                case 14:
                    sx = "10000000" + t; //$NON-NLS-1$
                    break;
                case 15:
                    sx = "1000000" + t; //$NON-NLS-1$
                    break;
                case 16:
                    sx = "100000" + t; //$NON-NLS-1$
                    break;
                case 17:
                    sx = "10000" + t; //$NON-NLS-1$
                    break;
                case 18:
                    sx = "1000" + t; //$NON-NLS-1$
                    break;
                case 19:
                    sx = "100" + t; //$NON-NLS-1$
                    break;
                case 20:
                    sx = "10" + t; //$NON-NLS-1$
                    break;
                case 21:
                    sx = "1" + t; //$NON-NLS-1$
                    break;
                }
            } else
                sx = Long.toString(x, 8);
            return printOFormat(sx);
        }

        /**
         * Format method for the o conversion character and int argument.
         * 
         * For o format, the flag character '-', means that the output should be left justified within the field. The
         * default is to pad with blanks on the left. The '#' flag character means that the output begins with a leading
         * 0 and the precision is increased by 1.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is to add no
         * padding. Padding is with blanks by default.
         * 
         * The precision, if set, is the minimum number of digits to appear. Padding is with leading 0s.
         * 
         * @param x the int to format.
         * @return the formatted String.
         */
        private String printOFormat(int x) {
            String sx = null;
            if (x == Integer.MIN_VALUE)
                sx = "20000000000"; //$NON-NLS-1$
            else if (x < 0) {
                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
                switch (t.length()) {
                case 1:
                    sx = "2000000000" + t; //$NON-NLS-1$
                    break;
                case 2:
                    sx = "200000000" + t; //$NON-NLS-1$
                    break;
                case 3:
                    sx = "20000000" + t; //$NON-NLS-1$
                    break;
                case 4:
                    sx = "2000000" + t; //$NON-NLS-1$
                    break;
                case 5:
                    sx = "200000" + t; //$NON-NLS-1$
                    break;
                case 6:
                    sx = "20000" + t; //$NON-NLS-1$
                    break;
                case 7:
                    sx = "2000" + t; //$NON-NLS-1$
                    break;
                case 8:
                    sx = "200" + t; //$NON-NLS-1$
                    break;
                case 9:
                    sx = "20" + t; //$NON-NLS-1$
                    break;
                case 10:
                    sx = "2" + t; //$NON-NLS-1$
                    break;
                case 11:
                    sx = "3" + t.substring(1); //$NON-NLS-1$
                    break;
                }
            } else
                sx = Integer.toString(x, 8);
            return printOFormat(sx);
        }

        /**
         * Utility method for formatting using the o conversion character.
         * 
         * @param sx the String to format, the result of converting a short, int, or long to a String.
         * @return the formatted String.
         */
        private String printOFormat(String sx) {
            int nLeadingZeros = 0;
            int nBlanks = 0;
            if (sx.equals("0") && precisionSet && precision == 0) //$NON-NLS-1$
                sx = ""; //$NON-NLS-1$
            if (precisionSet)
                nLeadingZeros = precision - sx.length();
            if (alternateForm)
                nLeadingZeros++;
            if (nLeadingZeros < 0)
                nLeadingZeros = 0;
            if (fieldWidthSet)
                nBlanks = fieldWidth - nLeadingZeros - sx.length();
            if (nBlanks < 0)
                nBlanks = 0;
            int n = nLeadingZeros + sx.length() + nBlanks;
            char[] ca = new char[n];
            int i;
            if (leftJustify) {
                for (i = 0; i < nLeadingZeros; i++)
                    ca[i] = '0';
                char[] csx = sx.toCharArray();
                for (int j = 0; j < csx.length; j++, i++)
                    ca[i] = csx[j];
                for (int j = 0; j < nBlanks; j++, i++)
                    ca[i] = ' ';
            } else {
                if (leadingZeros)
                    for (i = 0; i < nBlanks; i++)
                        ca[i] = '0';
                else
                    for (i = 0; i < nBlanks; i++)
                        ca[i] = ' ';
                for (int j = 0; j < nLeadingZeros; j++, i++)
                    ca[i] = '0';
                char[] csx = sx.toCharArray();
                for (int j = 0; j < csx.length; j++, i++)
                    ca[i] = csx[j];
            }
            return new String(ca);
        }

        /**
         * Format method for the c conversion character and char argument.
         * 
         * The only flag character that affects c format is the '-', meaning that the output should be left justified
         * within the field. The default is to pad with blanks on the left.
         * 
         * The field width is treated as the minimum number of characters to be printed. Padding is with blanks by
         * default. The default width is 1.
         * 
         * The precision, if set, is ignored.
         * 
         * @param x the char to format.
         * @return the formatted String.
         */
        private String printCFormat(char x) {
            int nPrint = 1;
            int width = fieldWidth;
            if (!fieldWidthSet)
                width = nPrint;
            char[] ca = new char[width];
            int i = 0;
            if (leftJustify) {
                ca[0] = x;
                for (i = 1; i <= width - nPrint; i++)
                    ca[i] = ' ';
            } else {
                for (i = 0; i < width - nPrint; i++)
                    ca[i] = ' ';
                ca[i] = x;
            }
            return new String(ca);
        }

        /**
         * Format method for the s conversion character and String argument.
         * 
         * The only flag character that affects s format is the '-', meaning that the output should be left justified
         * within the field. The default is to pad with blanks on the left.
         * 
         * The field width is treated as the minimum number of characters to be printed. The default is the smaller of
         * the number of characters in the the input and the precision. Padding is with blanks by default.
         * 
         * The precision, if set, specifies the maximum number of characters to be printed from the string. A null digit
         * string is treated as a 0. The default is not to set a maximum number of characters to be printed.
         * 
         * @param x the String to format.
         * @return the formatted String.
         */
        private String printSFormat(String x) {
            int nPrint = x.length();
            int width = fieldWidth;
            if (precisionSet && nPrint > precision)
                nPrint = precision;
            if (!fieldWidthSet)
                width = nPrint;
            int n = 0;
            if (width > nPrint)
                n += width - nPrint;
            if (nPrint >= x.length())
                n += x.length();
            else
                n += nPrint;
            char[] ca = new char[n];
            int i = 0;
            if (leftJustify) {
                if (nPrint >= x.length()) {
                    char[] csx = x.toCharArray();
                    for (i = 0; i < x.length(); i++)
                        ca[i] = csx[i];
                } else {
                    char[] csx = x.substring(0, nPrint).toCharArray();
                    for (i = 0; i < nPrint; i++)
                        ca[i] = csx[i];
                }
                for (int j = 0; j < width - nPrint; j++, i++)
                    ca[i] = ' ';
            } else {
                for (i = 0; i < width - nPrint; i++)
                    ca[i] = ' ';
                if (nPrint >= x.length()) {
                    char[] csx = x.toCharArray();
                    for (int j = 0; j < x.length(); i++, j++)
                        ca[i] = csx[j];
                } else {
                    char[] csx = x.substring(0, nPrint).toCharArray();
                    for (int j = 0; j < nPrint; i++, j++)
                        ca[i] = csx[j];
                }
            }
            return new String(ca);
        }

        /**
         * Check for a conversion character. If it is there, store it.
         * 
         * @param x the String to format.
         * @return <code>true</code> if the conversion character is there, and <code>false</code> otherwise.
         */
        private boolean setConversionCharacter() {
            /* idfgGoxXeEcs */
            boolean ret = false;
            conversionCharacter = '\0';
            if (pos < fmt.length()) {
                char c = fmt.charAt(pos);
                if (c == 'i' || c == 'd' || c == 'f' || c == 'g' || c == 'G' || c == 'o' || c == 'x' || c == 'X'
                        || c == 'e' || c == 'E' || c == 'c' || c == 's' || c == '%') {
                    conversionCharacter = c;
                    pos++;
                    ret = true;
                }
            }
            return ret;
        }

        /**
         * Check for an h, l, or L in a format. An L is used to control the minimum number of digits in an exponent when
         * using floating point formats. An l or h is used to control conversion of the input to a long or short,
         * respectively, before formatting. If any of these is present, store them.
         */
        private void setOptionalHL() {
            optionalh = false;
            optionall = false;
            optionalL = false;
            if (pos < fmt.length()) {
                char c = fmt.charAt(pos);
                if (c == 'h') {
                    optionalh = true;
                    pos++;
                } else if (c == 'l') {
                    optionall = true;
                    pos++;
                } else if (c == 'L') {
                    optionalL = true;
                    pos++;
                }
            }
        }

        /**
         * Set the precision.
         */
        private void setPrecision() {
            int firstPos = pos;
            precisionSet = false;
            if (pos < fmt.length() && fmt.charAt(pos) == '.') {
                pos++;
                if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) {
                    pos++;
                    if (!setPrecisionArgPosition()) {
                        variablePrecision = true;
                        precisionSet = true;
                    }
                    return;
                } else {
                    while (pos < fmt.length()) {
                        char c = fmt.charAt(pos);
                        if (Character.isDigit(c))
                            pos++;
                        else
                            break;
                    }
                    if (pos > firstPos + 1) {
                        String sz = fmt.substring(firstPos + 1, pos);
                        precision = Integer.parseInt(sz);
                        precisionSet = true;
                    }
                }
            }
        }

        /**
         * Set the field width.
         */
        private void setFieldWidth() {
            int firstPos = pos;
            fieldWidth = 0;
            fieldWidthSet = false;
            if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) {
                pos++;
                if (!setFieldWidthArgPosition()) {
                    variableFieldWidth = true;
                    fieldWidthSet = true;
                }
            } else {
                while (pos < fmt.length()) {
                    char c = fmt.charAt(pos);
                    if (Character.isDigit(c))
                        pos++;
                    else
                        break;
                }
                if (firstPos < pos && firstPos < fmt.length()) {
                    String sz = fmt.substring(firstPos, pos);
                    fieldWidth = Integer.parseInt(sz);
                    fieldWidthSet = true;
                }
            }
        }

        /**
         * Store the digits <code>n</code> in %n$ forms.
         */
        private void setArgPosition() {
            int xPos;
            for (xPos = pos; xPos < fmt.length(); xPos++) {
                if (!Character.isDigit(fmt.charAt(xPos)))
                    break;
            }
            if (xPos > pos && xPos < fmt.length()) {
                if (fmt.charAt(xPos) == '$') {
                    positionalSpecification = true;
                    argumentPosition = Integer.parseInt(fmt.substring(pos, xPos));
                    pos = xPos + 1;
                }
            }
        }

        /**
         * Store the digits <code>n</code> in *n$ forms.
         */
        private boolean setFieldWidthArgPosition() {
            boolean ret = false;
            int xPos;
            for (xPos = pos; xPos < fmt.length(); xPos++) {
                if (!Character.isDigit(fmt.charAt(xPos)))
                    break;
            }
            if (xPos > pos && xPos < fmt.length()) {
                if (fmt.charAt(xPos) == '$') {
                    positionalFieldWidth = true;
                    argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos));
                    pos = xPos + 1;
                    ret = true;
                }
            }
            return ret;
        }

        /**
         * Store the digits <code>n</code> in *n$ forms.
         */
        private boolean setPrecisionArgPosition() {
            boolean ret = false;
            int xPos;
            for (xPos = pos; xPos < fmt.length(); xPos++) {
                if (!Character.isDigit(fmt.charAt(xPos)))
                    break;
            }
            if (xPos > pos && xPos < fmt.length()) {
                if (fmt.charAt(xPos) == '$') {
                    positionalPrecision = true;
                    argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos));
                    pos = xPos + 1;
                    ret = true;
                }
            }
            return ret;
        }

        boolean isPositionalSpecification() {
            return positionalSpecification;
        }

        int getArgumentPosition() {
            return argumentPosition;
        }

        boolean isPositionalFieldWidth() {
            return positionalFieldWidth;
        }

        int getArgumentPositionForFieldWidth() {
            return argumentPositionForFieldWidth;
        }

        boolean isPositionalPrecision() {
            return positionalPrecision;
        }

        int getArgumentPositionForPrecision() {
            return argumentPositionForPrecision;
        }

        /**
         * Set flag characters, one of '-+#0 or a space.
         */
        private void setFlagCharacters() {
            /* '-+ #0 */
            thousands = false;
            leftJustify = false;
            leadingSign = false;
            leadingSpace = false;
            alternateForm = false;
            leadingZeros = false;
            for (; pos < fmt.length(); pos++) {
                char c = fmt.charAt(pos);
                if (c == '\'')
                    thousands = true;
                else if (c == '-') {
                    leftJustify = true;
                    leadingZeros = false;
                } else if (c == '+') {
                    leadingSign = true;
                    leadingSpace = false;
                } else if (c == ' ') {
                    if (!leadingSign)
                        leadingSpace = true;
                } else if (c == '#')
                    alternateForm = true;
                else if (c == '0') {
                    if (!leftJustify)
                        leadingZeros = true;
                } else
                    break;
            }
        }

        /**
         * The integer portion of the result of a decimal conversion (i, d, u, f, g, or G) will be formatted with
         * thousands' grouping characters. For other conversions the flag is ignored.
         */
        private boolean thousands = false;

        /**
         * The result of the conversion will be left-justified within the field.
         */
        private boolean leftJustify = false;

        /**
         * The result of a signed conversion will always begin with a sign (+ or -).
         */
        private boolean leadingSign = false;

        /**
         * Flag indicating that left padding with spaces is specified.
         */
        private boolean leadingSpace = false;

        /**
         * For an o conversion, increase the precision to force the first digit of the result to be a zero. For x (or X)
         * conversions, a non-zero result will have 0x (or 0X) prepended to it. For e, E, f, g, or G conversions, the
         * result will always contain a radix character, even if no digits follow the point. For g and G conversions,
         * trailing zeros will not be removed from the result.
         */
        private boolean alternateForm = false;

        /**
         * Flag indicating that left padding with zeroes is specified.
         */
        private boolean leadingZeros = false;

        /**
         * Flag indicating that the field width is *.
         */
        private boolean variableFieldWidth = false;

        /**
         * If the converted value has fewer bytes than the field width, it will be padded with spaces or zeroes.
         */
        private int fieldWidth = 0;

        /**
         * Flag indicating whether or not the field width has been set.
         */
        private boolean fieldWidthSet = false;

        /**
         * The minimum number of digits to appear for the d, i, o, u, x, or X conversions. The number of digits to
         * appear after the radix character for the e, E, and f conversions. The maximum number of significant digits
         * for the g and G conversions. The maximum number of bytes to be printed from a string in s and S conversions.
         */
        private int precision = 0;

        /** Default precision. */
        private final static int defaultDigits = 6;

        /**
         * Flag indicating that the precision is *.
         */
        private boolean variablePrecision = false;

        /**
         * Flag indicating whether or not the precision has been set.
         */
        private boolean precisionSet = false;

        /*
         */
        private boolean positionalSpecification = false;

        private int argumentPosition = 0;

        private boolean positionalFieldWidth = false;

        private int argumentPositionForFieldWidth = 0;

        private boolean positionalPrecision = false;

        private int argumentPositionForPrecision = 0;

        /**
         * Flag specifying that a following d, i, o, u, x, or X conversion character applies to a type short int.
         */
        private boolean optionalh = false;

        /**
         * Flag specifying that a following d, i, o, u, x, or X conversion character applies to a type lont int
         * argument.
         */
        private boolean optionall = false;

        /**
         * Flag specifying that a following e, E, f, g, or G conversion character applies to a type double argument.
         * This is a noop in Java.
         */
        private boolean optionalL = false;

        /** Control string type. */
        private char conversionCharacter = '\0';

        /**
         * Position within the control string. Used by the constructor.
         */
        private int pos = 0;

        /** Literal or control format string. */
        private String fmt;
    }

    /** Vector of control strings and format literals. */
    private Vector vFmt = new Vector();

    /** Character position. Used by the constructor. */
    private int cPos = 0;

    /** Character position. Used by the constructor. */
    private DecimalFormatSymbols dfs = null;
}
