content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software GmbH & Co. KG, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  //
  // (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 org.opencms.util;
  
  import org.opencms.main.CmsIllegalArgumentException;
  
  import java.text.DecimalFormatSymbols;
  import java.util.Enumeration;
  import java.util.Locale;
  import java.util.Vector;
  
  /**
   * 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).
   * <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>
   *
   * @since 6.0.0
   */
  public class PrintfFormat {
  
      /**
       *<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 final class ConversionSpecification {
  
          /** Default precision. */
          private static final int DEFAULT_DIGITS = 6;
  
          /**
           * 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 m_alternateForm;
  
          /** Internal variable. */
          private int m_argumentPosition;
  
          /** Internal variable. */
          private int m_argumentPositionForFieldWidth;
  
          /** Internal variable. */
          private int m_argumentPositionForPrecision;
  
          /** Control string type. */
          private char m_conversionCharacter;
  
          /**
           * If the converted value has fewer bytes than the
           * field width, it will be padded with spaces or
           * zeroes.
           */
          private int m_fieldWidth;
  
          /**
           * Flag indicating whether or not the field width
           * has been set.
           */
          private boolean m_fieldWidthSet;
  
          /** Literal or control format string. */
          private String m_fmt;
  
          /**
           * The result of a signed conversion will always
           * begin with a sign (+ or -).
           */
          private boolean m_leadingSign;
  
          /**
           * Flag indicating that left padding with spaces is
           * specified.
           */
          private boolean m_leadingSpace;
  
          /**
           * Flag indicating that left padding with zeroes is
           * specified.
           */
          private boolean m_leadingZeros;
  
          /**
           * The result of the conversion will be
           * left-justified within the field.
           */
          private boolean m_leftJustify;
  
          /**
           * Flag specifying that a following d, i, o, u, x,
           * or X conversion character applies to a type
           * short int.
           */
          private boolean m_optionalh;
  
          /**
           * Flag specifying that a following d, i, o, u, x,
           * or X conversion character applies to a type lont
           * int argument.
           */
          private boolean m_optionall;
  
          /**
           * 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 m_optionalL;
  
          /**
           * Position within the control string.  Used by
           * the constructor.
           */
          private int m_pos;
  
          /** Internal variable. */
          private boolean m_positionalFieldWidth;
  
          /** Internal variable. */
          private boolean m_positionalPrecision;
  
          /** Internal variable. */
          private boolean m_positionalSpecification;
  
          /**
           * 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 m_precision;
  
          /**
           * Flag indicating whether or not the precision has
           * been set.
           */
          private boolean m_precisionSet;
  
          /**
           * 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 m_thousands;
  
          /**
           * Flag indicating that the field width is *.
           */
          private boolean m_variableFieldWidth;
  
          /**
           * Flag indicating that the precision is *.
           */
          private boolean m_variablePrecision;
  
          /**
           * Constructor.  Used to prepare an instance
           * to hold a literal, not a control string.
           */
          ConversionSpecification() {
  
              // empty
          }
  
          /**
           * 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 CmsIllegalArgumentException if the
           *     input string is null, zero length, or
           *     otherwise malformed.
           */
          ConversionSpecification(String fmtArg)
          throws CmsIllegalArgumentException {
  
              if (fmtArg == null) {
                  throw new NullPointerException();
              }
              if (fmtArg.length() == 0) {
                  throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_CONTROL_STRING_LENGTH_0));
              }
              if (fmtArg.charAt(0) == '%') {
                  m_fmt = fmtArg;
                  m_pos = 1;
                  setArgPosition();
                  setFlagCharacters();
                  setFieldWidth();
                  setPrecision();
                  setOptionalHL();
                  if (setConversionCharacter()) {
                      if (m_pos == fmtArg.length()) {
                          if (m_leadingZeros && m_leftJustify) {
                              m_leadingZeros = false;
                          }
                          if (m_precisionSet && m_leadingZeros) {
                              if ((m_conversionCharacter == 'd')
                                  || (m_conversionCharacter == 'i')
                                  || (m_conversionCharacter == 'o')
                                  || (m_conversionCharacter == 'x')) {
                                  m_leadingZeros = false;
                              }
                          }
                      } else {
                          throw new CmsIllegalArgumentException(
                              Messages.get().container(Messages.ERR_INVALID_CONVERSION_SPEC_1, fmtArg));
                      }
                  } else {
                      throw new CmsIllegalArgumentException(
                          Messages.get().container(Messages.ERR_INVALID_CONVERSION_SPEC_1, fmtArg));
                  }
              } else {
                  throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_CONTROL_STRING_START_0));
              }
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          int getArgumentPosition() {
  
              return m_argumentPosition;
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          int getArgumentPositionForFieldWidth() {
  
              return m_argumentPositionForFieldWidth;
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          int getArgumentPositionForPrecision() {
  
              return m_argumentPositionForPrecision;
          }
  
          /**
           * Get the conversion character that tells what
           * type of control character this instance has.
           *
           * @return the conversion character.
           */
          char getConversionCharacter() {
  
              return m_conversionCharacter;
          }
  
          /**
           * 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 < m_fmt.length()) {
                  if (m_fmt.charAt(i) == '\\') {
                      i++;
                      if (i < m_fmt.length()) {
                          char c = m_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"));
                                  break;
                              case 'r':
                                  sb.append('\r');
                                  break;
                              case 't':
                                  sb.append('\t');
                                  break;
                              case 'v':
                                  sb.append((char)0x0b);
                                  break;
                              case '\\':
                                  sb.append('\\');
                                  break;
                              default:
                                  // noop
                          }
                          i++;
                      } else {
                          sb.append('\\');
                      }
                  } else {
                      i++;
                  }
              }
              return m_fmt;
          }
  
          /**
           * Format a double argument using this conversion
           * specification.
           * @param s the double to format.
           * @return the formatted String.
           * @exception CmsIllegalArgumentException if the
           *     conversion character is c, C, s, S, i, d,
           *     x, X, or o.
           */
          String internalsprintf(double s) throws CmsIllegalArgumentException {
  
              String s2 = "";
              switch (m_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 CmsIllegalArgumentException(Messages.get().container(
                          Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
                          "double",
                          Character.valueOf(m_conversionCharacter)));
              }
              return s2;
          }
  
          /**
           * Format an int argument using this conversion
           * specification.
           * @param s the int to format.
           * @return the formatted String.
           * @exception CmsIllegalArgumentException if the
           *     conversion character is f, e, E, g, or G.
           */
          String internalsprintf(int s) throws CmsIllegalArgumentException {
  
              String s2 = "";
              switch (m_conversionCharacter) {
                  case 'd':
                  case 'i':
                      if (m_optionalh) {
                          s2 = printDFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printDFormat((long)s);
                      } else {
                          s2 = printDFormat(s);
                      }
                      break;
                  case 'x':
                  case 'X':
                      if (m_optionalh) {
                          s2 = printXFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printXFormat((long)s);
                      } else {
                          s2 = printXFormat(s);
                      }
                      break;
                  case 'o':
                      if (m_optionalh) {
                          s2 = printOFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printOFormat((long)s);
                      } else {
                          s2 = printOFormat(s);
                      }
                      break;
                  case 'c':
                  case 'C':
                      s2 = printCFormat((char)s);
                      break;
                  default:
                      throw new CmsIllegalArgumentException(Messages.get().container(
                          Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
                          "int",
                          Character.valueOf(m_conversionCharacter)));
              }
              return s2;
          }
  
          /**
           * Format a long argument using this conversion
           * specification.
           * @param s the long to format.
           * @return the formatted String.
           * @exception CmsIllegalArgumentException if the
           *     conversion character is f, e, E, g, or G.
           */
          String internalsprintf(long s) throws CmsIllegalArgumentException {
  
              String s2 = "";
              switch (m_conversionCharacter) {
                  case 'd':
                  case 'i':
                      if (m_optionalh) {
                          s2 = printDFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printDFormat(s);
                      } else {
                          s2 = printDFormat((int)s);
                      }
                      break;
                  case 'x':
                  case 'X':
                      if (m_optionalh) {
                          s2 = printXFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printXFormat(s);
                      } else {
                          s2 = printXFormat((int)s);
                      }
                      break;
                  case 'o':
                      if (m_optionalh) {
                          s2 = printOFormat((short)s);
                      } else if (m_optionall) {
                          s2 = printOFormat(s);
                      } else {
                          s2 = printOFormat((int)s);
                      }
                      break;
                  case 'c':
                  case 'C':
                      s2 = printCFormat((char)s);
                      break;
                  default:
                      throw new CmsIllegalArgumentException(Messages.get().container(
                          Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
                          "long",
                          Character.valueOf(m_conversionCharacter)));
              }
              return s2;
          }
  
          /**
           * Format an Object argument using this conversion
           * specification.
           * @param s the Object to format.
           * @return the formatted String.
           * @exception CmsIllegalArgumentException if the
           *     conversion character is neither s nor S.
           */
          String internalsprintf(Object s) throws CmsIllegalArgumentException {
  
              String s2 = "";
              if ((m_conversionCharacter == 's') || (m_conversionCharacter == 'S')) {
                  s2 = printSFormat(s.toString());
              } else {
                  throw new CmsIllegalArgumentException(
                      Messages.get().container(
                          Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
                          "String",
                          Character.valueOf(m_conversionCharacter)));
              }
              return s2;
          }
  
          /**
           * Format a String argument using this conversion
           * specification.
           * @param s the String to format.
           * @return the formatted String.
           * @exception CmsIllegalArgumentException if the
           *   conversion character is neither s nor S.
           */
          String internalsprintf(String s) throws CmsIllegalArgumentException {
  
              String s2 = "";
              if ((m_conversionCharacter == 's') || (m_conversionCharacter == 'S')) {
                  s2 = printSFormat(s);
              } else {
                  throw new CmsIllegalArgumentException(Messages.get().container(
                      Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
                      "String",
                      Character.valueOf(m_conversionCharacter)));
              }
              return s2;
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          boolean isPositionalFieldWidth() {
  
              return m_positionalFieldWidth;
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          boolean isPositionalPrecision() {
  
              return m_positionalPrecision;
          }
  
          /**
           * Internal helper.<p>
           *
           * @return the result
           */
          boolean isPositionalSpecification() {
  
              return m_positionalSpecification;
          }
  
          /**
           * 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 m_variableFieldWidth;
          }
  
          /**
           * 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 m_variablePrecision;
          }
  
          /**
           * 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) {
                  m_leftJustify = true;
              }
              m_fieldWidthSet = true;
              m_fieldWidth = Math.abs(fw);
          }
  
          /**
           * Set the String for this instance.
           * @param s the String to store.
           */
          void setLiteral(String s) {
  
              m_fmt = s;
          }
  
          /**
           * Set the precision with an argument.  A
           * negative precision will be changed to zero.
           * @param pr the precision.
           */
          void setPrecisionWithArg(int pr) {
  
              m_precisionSet = true;
              m_precision = Math.max(pr, 0);
          }
  
          /**
           * 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 (m_fieldWidthSet) {
                  int i, j, nBlanks;
                  if (m_leftJustify) {
                      nBlanks = m_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 (!m_leadingZeros || noDigits) {
                      nBlanks = m_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 (m_leadingZeros) {
                      nBlanks = m_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;
          }
  
          /**
           * 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;
          }
  
          /**
           * 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.
           *
           * @param x the x parameter
           * @param eChar the eChar parameter
           * @return the result
           */
          private char[] eFormatDigits(double x, char eChar) {
  
              char[] ca1, ca2, ca3;
              // int defaultDigits=6;
              String sx;
              int i, j, k, p;
              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 (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 (m_precisionSet) {
                  p = m_precision;
              } else {
                  p = DEFAULT_DIGITS - 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) && !m_optionalL) {
                  eSize = 4;
              } else {
                  eSize = 5;
              }
              if (m_alternateForm || !m_precisionSet || (m_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 (m_alternateForm || !m_precisionSet || (m_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;
                      default:
                          // noop
                  }
                  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;
                  default:
                      // noop
              }
              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;
                  default:
                      // noop
              }
              int nZeros = 0;
              if (!m_leftJustify && m_leadingZeros) {
                  int xThousands = 0;
                  if (m_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 (m_fieldWidthSet) {
                      nZeros = m_fieldWidth - ca2.length;
                  }
                  if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
                      nZeros--;
                  }
                  nZeros -= xThousands;
                  if (nZeros < 0) {
                      nZeros = 0;
                  }
              }
              j = 0;
              if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
                  ca3 = new char[ca2.length + nZeros + 1];
                  j++;
              } else {
                  ca3 = new char[ca2.length + nZeros];
              }
              if (!minusSign) {
                  if (m_leadingSign) {
                      ca3[0] = '+';
                  }
                  if (m_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] = m_dfs.getDecimalSeparator();
              }
              char[] ca4 = ca3;
              if (m_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] = m_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;
          }
  
          /**
           * 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) {
  
              char[] ca4, ca5;
              if (Double.isInfinite(x)) {
                  if (x == Double.POSITIVE_INFINITY) {
                      if (m_leadingSign) {
                          ca4 = "+Inf".toCharArray();
                      } else if (m_leadingSpace) {
                          ca4 = " Inf".toCharArray();
                      } else {
                          ca4 = "Inf".toCharArray();
                      }
                  } else {
                      ca4 = "-Inf".toCharArray();
                  }
              } else if (Double.isNaN(x)) {
                  if (m_leadingSign) {
                      ca4 = "+NaN".toCharArray();
                  } else if (m_leadingSpace) {
                      ca4 = " NaN".toCharArray();
                  } else {
                      ca4 = "NaN".toCharArray();
                  }
              } else {
                  ca4 = eFormatDigits(x, eChar);
              }
              ca5 = applyFloatPadding(ca4, false);
              return new String(ca5);
          }
  
          /**
           * 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.
           *
           * @param x the paramater
           * @return the result
           */
          private char[] fFormatDigits(double x) {
  
              // int defaultDigits=6;
              String sx;
              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 (m_precisionSet) {
                  p = m_precision;
              } else {
                  p = DEFAULT_DIGITS - 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 (m_alternateForm || !m_precisionSet || (m_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 (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
                          ca4 = new char[n1In + expon + p + 1];
                      } else {
                          ca4 = new char[n1In + expon];
                      }
                      j = 0;
                  } else {
                      if (m_alternateForm || !m_precisionSet || (m_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 (m_alternateForm || !m_precisionSet || (m_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 (!m_leftJustify && m_leadingZeros) {
                  int xThousands = 0;
                  if (m_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 (m_fieldWidthSet) {
                      nZeros = m_fieldWidth - ca4.length;
                  }
                  if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
                      nZeros--;
                  }
                  nZeros -= xThousands;
                  if (nZeros < 0) {
                      nZeros = 0;
                  }
              }
              j = 0;
              if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
                  ca5 = new char[ca4.length + nZeros + 1];
                  j++;
              } else {
                  ca5 = new char[ca4.length + nZeros];
              }
              if (!minusSign) {
                  if (m_leadingSign) {
                      ca5[0] = '+';
                  }
                  if (m_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] = m_dfs.getDecimalSeparator();
              }
              char[] ca6 = ca5;
              if (m_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] = m_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) {
  
              char[] ca6, ca7;
              if (Double.isInfinite(x)) {
                  if (x == Double.POSITIVE_INFINITY) {
                      if (m_leadingSign) {
                          ca6 = "+Inf".toCharArray();
                      } else if (m_leadingSpace) {
                          ca6 = " Inf".toCharArray();
                      } else {
                          ca6 = "Inf".toCharArray();
                      }
                  } else {
                      ca6 = "-Inf".toCharArray();
                  }
              } else if (Double.isNaN(x)) {
                  if (m_leadingSign) {
                      ca6 = "+NaN".toCharArray();
                  } else if (m_leadingSpace) {
                      ca6 = " NaN".toCharArray();
                  } else {
                      ca6 = "NaN".toCharArray();
                  }
              } else {
                  ca6 = fFormatDigits(x);
              }
              ca7 = applyFloatPadding(ca6, false);
              return new String(ca7);
          }
  
          /**
           * 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 = m_fieldWidth;
              if (!m_fieldWidthSet) {
                  width = nPrint;
              }
              char[] ca = new char[width];
              int i = 0;
              if (m_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 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));
          }
  
          /**
           * 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 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));
          }
  
          /**
           * 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") && m_precisionSet && (m_precision == 0)) {
                  sx = "";
              }
              if (!neg) {
                  if (m_precisionSet && (sx.length() < m_precision)) {
                      nLeadingZeros = m_precision - sx.length();
                  }
              } else {
                  if (m_precisionSet && ((sx.length() - 1) < m_precision)) {
                      nLeadingZeros = (m_precision - sx.length()) + 1;
                  }
              }
              if (nLeadingZeros < 0) {
                  nLeadingZeros = 0;
              }
              if (m_fieldWidthSet) {
                  nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
                  if (!neg && (m_leadingSign || m_leadingSpace)) {
                      nBlanks--;
                  }
              }
              if (nBlanks < 0) {
                  nBlanks = 0;
              }
              if (m_leadingSign) {
                  n++;
              } else if (m_leadingSpace) {
                  n++;
              }
              n += nBlanks;
              n += nLeadingZeros;
              n += sx.length();
              char[] ca = new char[n];
              if (m_leftJustify) {
                  if (neg) {
                      ca[i++] = '-';
                  } else if (m_leadingSign) {
                      ca[i++] = '+';
                  } else if (m_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 (!m_leadingZeros) {
                      for (i = 0; i < nBlanks; i++) {
                          ca[i] = ' ';
                      }
                      if (neg) {
                          ca[i++] = '-';
                      } else if (m_leadingSign) {
                          ca[i++] = '+';
                      } else if (m_leadingSpace) {
                          ca[i++] = ' ';
                      }
                  } else {
                      if (neg) {
                          ca[i++] = '-';
                      } else if (m_leadingSign) {
                          ca[i++] = '+';
                      } else if (m_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 e or E conversion
           * character.
           * @param x the double to format.
           * @return the formatted String.
           */
          private String printEFormat(double x) {
  
              if (m_conversionCharacter == 'e') {
                  return eFormatString(x, 'e');
              } else {
                  return eFormatString(x, 'E');
              }
          }
  
          /**
           * 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 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 = m_precision;
              int i;
              char[] ca4, ca5;
              if (Double.isInfinite(x)) {
                  if (x == Double.POSITIVE_INFINITY) {
                      if (m_leadingSign) {
                          ca4 = "+Inf".toCharArray();
                      } else if (m_leadingSpace) {
                          ca4 = " Inf".toCharArray();
                      } else {
                          ca4 = "Inf".toCharArray();
                      }
                  } else {
                      ca4 = "-Inf".toCharArray();
                  }
              } else if (Double.isNaN(x)) {
                  if (m_leadingSign) {
                      ca4 = "+NaN".toCharArray();
                  } else if (m_leadingSpace) {
                      ca4 = " NaN".toCharArray();
                  } else {
                      ca4 = "NaN".toCharArray();
                  }
              } else {
                  if (!m_precisionSet) {
                      m_precision = DEFAULT_DIGITS;
                  }
                  if (m_precision == 0) {
                      m_precision = 1;
                  }
                  int ePos = -1;
                  if (m_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 (!m_alternateForm) {
                      if ((expon >= -4) && (expon < m_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";
                      } else if (!Character.isDigit(sy.charAt(i))) {
                          sz = sy.substring(0, i + 1) + "0";
                      } else {
                          sz = sy.substring(0, i + 1);
                      }
                      if ((expon >= -4) && (expon < m_precision)) {
                          ret = sz;
                      } else {
                          ret = sz + sx.substring(ePos);
                      }
                  } else {
                      if ((expon >= -4) && (expon < m_precision)) {
                          ret = fFormatString(x).trim();
                      } else {
                          ret = sx;
                      }
                  }
                  // leading space was trimmed off during
                  // construction
                  if (m_leadingSpace) {
                      if (x >= 0) {
                          ret = " " + ret;
                      }
                  }
                  ca4 = ret.toCharArray();
              }
              // Pad with blanks or zeros.
              ca5 = applyFloatPadding(ca4, false);
              m_precision = savePrecision;
              return new String(ca5);
          }
  
          /**
           * 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";
              } else if (x < 0) {
                  String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
                  switch (t.length()) {
                      case 1:
                          sx = "2000000000" + t;
                          break;
                      case 2:
                          sx = "200000000" + t;
                          break;
                      case 3:
                          sx = "20000000" + t;
                          break;
                      case 4:
                          sx = "2000000" + t;
                          break;
                      case 5:
                          sx = "200000" + t;
                          break;
                      case 6:
                          sx = "20000" + t;
                          break;
                      case 7:
                          sx = "2000" + t;
                          break;
                      case 8:
                          sx = "200" + t;
                          break;
                      case 9:
                          sx = "20" + t;
                          break;
                      case 10:
                          sx = "2" + t;
                          break;
                      case 11:
                          sx = "3" + t.substring(1);
                          break;
                      default:
                          // noop
                  }
              } else {
                  sx = Integer.toString(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";
              } else if (x < 0) {
                  String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
                  switch (t.length()) {
                      case 1:
                          sx = "100000000000000000000" + t;
                          break;
                      case 2:
                          sx = "10000000000000000000" + t;
                          break;
                      case 3:
                          sx = "1000000000000000000" + t;
                          break;
                      case 4:
                          sx = "100000000000000000" + t;
                          break;
                      case 5:
                          sx = "10000000000000000" + t;
                          break;
                      case 6:
                          sx = "1000000000000000" + t;
                          break;
                      case 7:
                          sx = "100000000000000" + t;
                          break;
                      case 8:
                          sx = "10000000000000" + t;
                          break;
                      case 9:
                          sx = "1000000000000" + t;
                          break;
                      case 10:
                          sx = "100000000000" + t;
                          break;
                      case 11:
                          sx = "10000000000" + t;
                          break;
                      case 12:
                          sx = "1000000000" + t;
                          break;
                      case 13:
                          sx = "100000000" + t;
                          break;
                      case 14:
                          sx = "10000000" + t;
                          break;
                      case 15:
                          sx = "1000000" + t;
                          break;
                      case 16:
                          sx = "100000" + t;
                          break;
                      case 17:
                          sx = "10000" + t;
                          break;
                      case 18:
                          sx = "1000" + t;
                          break;
                      case 19:
                          sx = "100" + t;
                          break;
                      case 20:
                          sx = "10" + t;
                          break;
                      case 21:
                          sx = "1" + t;
                          break;
                      default:
                          // noop
                  }
              } else {
                  sx = Long.toString(x, 8);
              }
              return printOFormat(sx);
          }
  
          /**
           * 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";
              } else if (x < 0) {
                  String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
                  switch (t.length()) {
                      case 1:
                          sx = "10000" + t;
                          break;
                      case 2:
                          sx = "1000" + t;
                          break;
                      case 3:
                          sx = "100" + t;
                          break;
                      case 4:
                          sx = "10" + t;
                          break;
                      case 5:
                          sx = "1" + t;
                          break;
                      default:
                          // noop
                  }
              } 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") && m_precisionSet && (m_precision == 0)) {
                  sx = "";
              }
              if (m_precisionSet) {
                  nLeadingZeros = m_precision - sx.length();
              }
              if (m_alternateForm) {
                  nLeadingZeros++;
              }
              if (nLeadingZeros < 0) {
                  nLeadingZeros = 0;
              }
              if (m_fieldWidthSet) {
                  nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
              }
              if (nBlanks < 0) {
                  nBlanks = 0;
              }
              int n = nLeadingZeros + sx.length() + nBlanks;
              char[] ca = new char[n];
              int i;
              if (m_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 (m_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 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 = m_fieldWidth;
              if (m_precisionSet && (nPrint > m_precision)) {
                  nPrint = m_precision;
              }
              if (!m_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 (m_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);
          }
  
          /**
           * 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";
              } else if (x < 0) {
                  String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
                  switch (t.length()) {
                      case 1:
                          sx = "8000000" + t;
                          break;
                      case 2:
                          sx = "800000" + t;
                          break;
                      case 3:
                          sx = "80000" + t;
                          break;
                      case 4:
                          sx = "8000" + t;
                          break;
                      case 5:
                          sx = "800" + t;
                          break;
                      case 6:
                          sx = "80" + t;
                          break;
                      case 7:
                          sx = "8" + t;
                          break;
                      case 8:
                          switch (t.charAt(0)) {
                              case '1':
                                  sx = "9" + t.substring(1, 8);
                                  break;
                              case '2':
                                  sx = "a" + t.substring(1, 8);
                                  break;
                              case '3':
                                  sx = "b" + t.substring(1, 8);
                                  break;
                              case '4':
                                  sx = "c" + t.substring(1, 8);
                                  break;
                              case '5':
                                  sx = "d" + t.substring(1, 8);
                                  break;
                              case '6':
                                  sx = "e" + t.substring(1, 8);
                                  break;
                              case '7':
                                  sx = "f" + t.substring(1, 8);
                                  break;
                              default:
                                  // noop
                          }
                          break;
                      default:
                          // noop
                  }
              } else {
                  sx = Integer.toString(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";
              } else if (x < 0) {
                  String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
                  switch (t.length()) {
                      case 1:
                          sx = "800000000000000" + t;
                          break;
                      case 2:
                          sx = "80000000000000" + t;
                          break;
                      case 3:
                          sx = "8000000000000" + t;
                          break;
                      case 4:
                          sx = "800000000000" + t;
                          break;
                      case 5:
                          sx = "80000000000" + t;
                          break;
                      case 6:
                          sx = "8000000000" + t;
                          break;
                      case 7:
                          sx = "800000000" + t;
                          break;
                      case 8:
                          sx = "80000000" + t;
                          break;
                      case 9:
                          sx = "8000000" + t;
                          break;
                      case 10:
                          sx = "800000" + t;
                          break;
                      case 11:
                          sx = "80000" + t;
                          break;
                      case 12:
                          sx = "8000" + t;
                          break;
                      case 13:
                          sx = "800" + t;
                          break;
                      case 14:
                          sx = "80" + t;
                          break;
                      case 15:
                          sx = "8" + t;
                          break;
                      case 16:
                          switch (t.charAt(0)) {
                              case '1':
                                  sx = "9" + t.substring(1, 16);
                                  break;
                              case '2':
                                  sx = "a" + t.substring(1, 16);
                                  break;
                              case '3':
                                  sx = "b" + t.substring(1, 16);
                                  break;
                              case '4':
                                  sx = "c" + t.substring(1, 16);
                                  break;
                              case '5':
                                  sx = "d" + t.substring(1, 16);
                                  break;
                              case '6':
                                  sx = "e" + t.substring(1, 16);
                                  break;
                              case '7':
                                  sx = "f" + t.substring(1, 16);
                                  break;
                              default:
                                  // noop
                          }
                          break;
                      default:
                          // noop
                  }
              } else {
                  sx = Long.toString(x, 16);
              }
              return printXFormat(sx);
          }
  
          /**
           * 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";
              } else if (x < 0) {
                  String t;
                  if (x == Short.MIN_VALUE) {
                      t = "0";
                  } 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;
                          break;
                      case 2:
                          sx = "80" + t;
                          break;
                      case 3:
                          sx = "8" + t;
                          break;
                      case 4:
                          switch (t.charAt(0)) {
                              case '1':
                                  sx = "9" + t.substring(1, 4);
                                  break;
                              case '2':
                                  sx = "a" + t.substring(1, 4);
                                  break;
                              case '3':
                                  sx = "b" + t.substring(1, 4);
                                  break;
                              case '4':
                                  sx = "c" + t.substring(1, 4);
                                  break;
                              case '5':
                                  sx = "d" + t.substring(1, 4);
                                  break;
                              case '6':
                                  sx = "e" + t.substring(1, 4);
                                  break;
                              case '7':
                                  sx = "f" + t.substring(1, 4);
                                  break;
                              default:
                                  // noop
                          }
                          break;
                      default:
                          // noop
                  }
              } 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") && m_precisionSet && (m_precision == 0)) {
                  sx = "";
              }
              if (m_precisionSet) {
                  nLeadingZeros = m_precision - sx.length();
              }
              if (nLeadingZeros < 0) {
                  nLeadingZeros = 0;
              }
              if (m_fieldWidthSet) {
                  nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
                  if (m_alternateForm) {
                      nBlanks = nBlanks - 2;
                  }
              }
              if (nBlanks < 0) {
                  nBlanks = 0;
              }
              int n = 0;
              if (m_alternateForm) {
                  n += 2;
              }
              n += nLeadingZeros;
              n += sx.length();
              n += nBlanks;
              char[] ca = new char[n];
              int i = 0;
              if (m_leftJustify) {
                  if (m_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 (!m_leadingZeros) {
                      for (int j = 0; j < nBlanks; j++, i++) {
                          ca[i] = ' ';
                      }
                  }
                  if (m_alternateForm) {
                      ca[i++] = '0';
                      ca[i++] = 'x';
                  }
                  if (m_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 (m_conversionCharacter == 'X') {
                  caReturn = caReturn.toUpperCase();
              }
              return caReturn;
          }
  
          /**
           * Store the digits <code>n</code> in %n$ forms.
           */
          private void setArgPosition() {
  
              int xPos;
              for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
                  if (!Character.isDigit(m_fmt.charAt(xPos))) {
                      break;
                  }
              }
              if ((xPos > m_pos) && (xPos < m_fmt.length())) {
                  if (m_fmt.charAt(xPos) == '$') {
                      m_positionalSpecification = true;
                      m_argumentPosition = Integer.parseInt(m_fmt.substring(m_pos, xPos));
                      m_pos = xPos + 1;
                  }
              }
          }
  
          /**
           * Check for a conversion character.  If it is
           * there, store it.
           * @return <code>true</code> if the conversion
           *     character is there, and
           *     <code>false</code> otherwise.
           */
          private boolean setConversionCharacter() {
  
              /* idfgGoxXeEcs */
              boolean ret = false;
              m_conversionCharacter = '\0';
              if (m_pos < m_fmt.length()) {
                  char c = m_fmt.charAt(m_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 == '%')) {
                      m_conversionCharacter = c;
                      m_pos++;
                      ret = true;
                  }
              }
              return ret;
          }
  
          /**
           * Set the field width.
           */
          private void setFieldWidth() {
  
              int firstPos = m_pos;
              m_fieldWidth = 0;
              m_fieldWidthSet = false;
              if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '*')) {
                  m_pos++;
                  if (!setFieldWidthArgPosition()) {
                      m_variableFieldWidth = true;
                      m_fieldWidthSet = true;
                  }
              } else {
                  while (m_pos < m_fmt.length()) {
                      char c = m_fmt.charAt(m_pos);
                      if (Character.isDigit(c)) {
                          m_pos++;
                      } else {
                          break;
                      }
                  }
                  if ((firstPos < m_pos) && (firstPos < m_fmt.length())) {
                      String sz = m_fmt.substring(firstPos, m_pos);
                      m_fieldWidth = Integer.parseInt(sz);
                      m_fieldWidthSet = true;
                  }
              }
          }
  
          /**
           * Store the digits <code>n</code> in *n$ forms.
           *
           * @return the result
           */
          private boolean setFieldWidthArgPosition() {
  
              boolean ret = false;
              int xPos;
              for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
                  if (!Character.isDigit(m_fmt.charAt(xPos))) {
                      break;
                  }
              }
              if ((xPos > m_pos) && (xPos < m_fmt.length())) {
                  if (m_fmt.charAt(xPos) == '$') {
                      m_positionalFieldWidth = true;
                      m_argumentPositionForFieldWidth = Integer.parseInt(m_fmt.substring(m_pos, xPos));
                      m_pos = xPos + 1;
                      ret = true;
                  }
              }
              return ret;
          }
  
          /**
           * Set flag characters, one of '-+#0 or a space.
           */
          private void setFlagCharacters() {
  
              /* '-+ #0 */
              m_thousands = false;
              m_leftJustify = false;
              m_leadingSign = false;
              m_leadingSpace = false;
              m_alternateForm = false;
              m_leadingZeros = false;
              for (; m_pos < m_fmt.length(); m_pos++) {
                  char c = m_fmt.charAt(m_pos);
                  if (c == '\'') {
                      m_thousands = true;
                  } else if (c == '-') {
                      m_leftJustify = true;
                      m_leadingZeros = false;
                  } else if (c == '+') {
                      m_leadingSign = true;
                      m_leadingSpace = false;
                  } else if (c == ' ') {
                      if (!m_leadingSign) {
                          m_leadingSpace = true;
                      }
                  } else if (c == '#') {
                      m_alternateForm = true;
                  } else if (c == '0') {
                      if (!m_leftJustify) {
                          m_leadingZeros = true;
                      }
                  } else {
                      break;
                  }
              }
          }
  
          /**
           * 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() {
  
              m_optionalh = false;
              m_optionall = false;
              m_optionalL = false;
              if (m_pos < m_fmt.length()) {
                  char c = m_fmt.charAt(m_pos);
                  if (c == 'h') {
                      m_optionalh = true;
                      m_pos++;
                  } else if (c == 'l') {
                      m_optionall = true;
                      m_pos++;
                  } else if (c == 'L') {
                      m_optionalL = true;
                      m_pos++;
                  }
              }
          }
  
          /**
           * Set the precision.
           */
          private void setPrecision() {
  
              int firstPos = m_pos;
              m_precisionSet = false;
              if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '.')) {
                  m_pos++;
                  if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '*')) {
                      m_pos++;
                      if (!setPrecisionArgPosition()) {
                          m_variablePrecision = true;
                          m_precisionSet = true;
                      }
                      return;
                  } else {
                      while (m_pos < m_fmt.length()) {
                          char c = m_fmt.charAt(m_pos);
                          if (Character.isDigit(c)) {
                              m_pos++;
                          } else {
                              break;
                          }
                      }
                      if (m_pos > (firstPos + 1)) {
                          String sz = m_fmt.substring(firstPos + 1, m_pos);
                          m_precision = Integer.parseInt(sz);
                          m_precisionSet = true;
                      }
                  }
              }
          }
  
          /**
           * Store the digits <code>n</code> in *n$ forms.
           *
           * @return the result
           */
          private boolean setPrecisionArgPosition() {
  
              boolean ret = false;
              int xPos;
              for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
                  if (!Character.isDigit(m_fmt.charAt(xPos))) {
                      break;
                  }
              }
              if ((xPos > m_pos) && (xPos < m_fmt.length())) {
                  if (m_fmt.charAt(xPos) == '$') {
                      m_positionalPrecision = true;
                      m_argumentPositionForPrecision = Integer.parseInt(m_fmt.substring(m_pos, xPos));
                      m_pos = xPos + 1;
                      ret = true;
                  }
              }
              return ret;
          }
  
          /**
           * 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
           * @param ca the ca parameter
           * @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;
                      default:
                          // noop
                  }
              }
              return carry;
          }
      }
  
      /** Character position. Used by the constructor. */
      DecimalFormatSymbols m_dfs;
  
      /** Character position. Used by the constructor. */
      private int m_cPos;
  
      /** Vector of control strings and format literals. */
      private Vector<ConversionSpecification> m_vFmt = new Vector<ConversionSpecification>();
  
      /**
       * 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 locale the locale
       * @param fmtArg  Control string.
       * @exception CmsIllegalArgumentException if the control
       * string is null, zero length, or otherwise
       * malformed.
       */
      public PrintfFormat(Locale locale, String fmtArg)
      throws CmsIllegalArgumentException {
  
          m_dfs = new DecimalFormatSymbols(locale);
          int ePos = 0;
          ConversionSpecification sFmt = null;
          String unCS = nonControl(fmtArg, 0);
          if (unCS != null) {
              sFmt = new ConversionSpecification();
              sFmt.setLiteral(unCS);
              m_vFmt.addElement(sFmt);
          }
          while ((m_cPos != -1) && (m_cPos < fmtArg.length())) {
              for (ePos = m_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(m_cPos, ePos));
              m_vFmt.addElement(sFmt);
              unCS = nonControl(fmtArg, ePos);
              if (unCS != null) {
                  sFmt = new ConversionSpecification();
                  sFmt.setLiteral(unCS);
                  m_vFmt.addElement(sFmt);
              }
          }
      }
  
      /**
       * 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);
      }
  
      /**
       * Format nothing.  Just use the control string.
       * @return  the formatted String.
       */
      public String sprintf() {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              }
          }
          return sb.toString();
      }
  
      /**
       * Format a double.
       * @param x The double to format.
       * @return  The formatted String.
       * @exception CmsIllegalArgumentException if the
       *     conversion character is c, C, s, S,
       *     d, d, x, X, or o.
       */
      public String sprintf(double x) throws CmsIllegalArgumentException {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } else {
                  sb.append(cs.internalsprintf(x));
              }
          }
          return sb.toString();
      }
  
      /**
       * Format an int.
       * @param x The int to format.
       * @return  The formatted String.
       * @exception CmsIllegalArgumentException if the
       *     conversion character is f, e, E, g, G, s,
       *     or S.
       */
      public String sprintf(int x) throws CmsIllegalArgumentException {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } else {
                  sb.append(cs.internalsprintf(x));
              }
          }
          return sb.toString();
      }
  
      /**
       * Format an long.
       * @param x The long to format.
       * @return  The formatted String.
       * @exception CmsIllegalArgumentException if the
       *     conversion character is f, e, E, g, G, s,
       *     or S.
       */
      public String sprintf(long x) throws CmsIllegalArgumentException {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } 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 CmsIllegalArgumentException if the
       *    conversion character is inappropriate for
       *    formatting an unwrapped value.
       */
      public String sprintf(Object x) throws CmsIllegalArgumentException {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } 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();
      }
  
      /**
       * 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<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          int i = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } 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 a String.
       * @param x The String to format.
       * @return  The formatted String.
       * @exception CmsIllegalArgumentException if the
       *   conversion character is neither s nor S.
       */
      public String sprintf(String x) throws CmsIllegalArgumentException {
  
          Enumeration<ConversionSpecification> e = m_vFmt.elements();
          ConversionSpecification cs = null;
          char c = 0;
          StringBuffer sb = new StringBuffer();
          while (e.hasMoreElements()) {
              cs = e.nextElement();
              c = cs.getConversionCharacter();
              if (c == '\0') {
                  sb.append(cs.getLiteral());
              } else if (c == '%') {
                  sb.append("%");
              } else {
                  sb.append(cs.internalsprintf(x));
              }
          }
          return sb.toString();
      }
  
      /**
       * 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) {
  
          m_cPos = s.indexOf("%", start);
          if (m_cPos == -1) {
              m_cPos = s.length();
          }
          return s.substring(start, m_cPos);
      }
  }
  