001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028//
029// (c) 2000 Sun Microsystems, Inc.
030// ALL RIGHTS RESERVED
031//
032// License Grant-
033//
034//
035// Permission to use, copy, modify, and distribute this Software and its
036// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
037// hereby granted.
038//
039// This Software is provided "AS IS".  All express warranties, including any
040// implied warranty of merchantability, satisfactory quality, fitness for a
041// particular purpose, or non-infringement, are disclaimed, except to the extent
042// that such disclaimers are held to be legally invalid.
043//
044// You acknowledge that Software is not designed, licensed or intended for use in
045// the design, construction, operation or maintenance of any nuclear facility
046// ("High Risk Activities").  Sun disclaims any express or implied warranty of
047// fitness for such uses.
048//
049// Please refer to the file http://www.sun.com/policies/trademarks/ for further
050// important trademark information and to
051// http://java.sun.com/nav/business/index.html for further important licensing
052// information for the Java Technology.
053//
054
055package org.opencms.util;
056
057import org.opencms.main.CmsIllegalArgumentException;
058
059import java.text.DecimalFormatSymbols;
060import java.util.Enumeration;
061import java.util.Locale;
062import java.util.Vector;
063
064/**
065 * PrintfFormat allows the formatting of an array of
066 * objects embedded within a string.  Primitive types
067 * must be passed using wrapper types.  The formatting
068 * is controlled by a control string.
069 *<p>
070 * A control string is a Java string that contains a
071 * control specification.  The control specification
072 * starts at the first percent sign (%) in the string,
073 * provided that this percent sign
074 *<ol>
075 *<li>is not escaped protected by a matching % or is
076 * not an escape % character,
077 *<li>is not at the end of the format string, and
078 *<li>precedes a sequence of characters that parses as
079 * a valid control specification.
080 *</ol>
081 *</p><p>
082 * A control specification usually takes the form:
083 *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
084 *                { [hlL] }+ [idfgGoxXeEcs]
085 *</pre>
086 * There are variants of this basic form that are
087 * discussed below.</p>
088 *<p>
089 * The format is composed of zero or more directives
090 * defined as follows:
091 *<ul>
092 *<li>ordinary characters, which are simply copied to
093 * the output stream;
094 *<li>escape sequences, which represent non-graphic
095 * characters; and
096 *<li>conversion specifications,  each of which
097 * results in the fetching of zero or more arguments.
098 *</ul></p>
099 *<p>
100 * The results are undefined if there are insufficient
101 * arguments for the format.  Usually an unchecked
102 * exception will be thrown.  If the format is
103 * exhausted while arguments remain, the excess
104 * arguments are evaluated but are otherwise ignored.
105 * In format strings containing the % form of
106 * conversion specifications, each argument in the
107 * argument list is used exactly once.</p>
108 * <p>
109 * Conversions can be applied to the <code>n</code>th
110 * argument after the format in the argument list,
111 * rather than to the next unused argument.  In this
112 * case, the conversion characer % is replaced by the
113 * sequence %<code>n</code>$, where <code>n</code> is
114 * a decimal integer giving the position of the
115 * argument in the argument list.</p>
116 * <p>
117 * In format strings containing the %<code>n</code>$
118 * form of conversion specifications, each argument
119 * in the argument list is used exactly once.</p>
120 *
121 *<h4>Escape Sequences</h4>
122 *<p>
123 * The following table lists escape sequences and
124 * associated actions on display devices capable of
125 * the action.
126 *<table>
127 *<tr><th align=left>Sequence</th>
128 *    <th align=left>Name</th>
129 *    <th align=left>Description</th></tr>
130 *<tr><td>\\</td><td>backlash</td><td>None.
131 *</td></tr>
132 *<tr><td>\a</td><td>alert</td><td>Attempts to alert
133 *          the user through audible or visible
134 *          notification.
135 *</td></tr>
136 *<tr><td>\b</td><td>backspace</td><td>Moves the
137 *          printing position to one column before
138 *          the current position, unless the
139 *          current position is the start of a line.
140 *</td></tr>
141 *<tr><td>\f</td><td>form-feed</td><td>Moves the
142 *          printing position to the initial
143 *          printing position of the next logical
144 *          page.
145 *</td></tr>
146 *<tr><td>\n</td><td>newline</td><td>Moves the
147 *          printing position to the start of the
148 *          next line.
149 *</td></tr>
150 *<tr><td>\r</td><td>carriage-return</td><td>Moves
151 *          the printing position to the start of
152 *          the current line.
153 *</td></tr>
154 *<tr><td>\t</td><td>tab</td><td>Moves the printing
155 *          position to the next implementation-
156 *          defined horizontal tab position.
157 *</td></tr>
158 *<tr><td>\v</td><td>vertical-tab</td><td>Moves the
159 *          printing position to the start of the
160 *          next implementation-defined vertical
161 *          tab position.
162 *</td></tr>
163 *</table></p>
164 *<h4>Conversion Specifications</h4>
165 *<p>
166 * Each conversion specification is introduced by
167 * the percent sign character (%).  After the character
168 * %, the following appear in sequence:</p>
169 *<p>
170 * Zero or more flags (in any order), which modify the
171 * meaning of the conversion specification.</p>
172 *<p>
173 * An optional minimum field width.  If the converted
174 * value has fewer characters than the field width, it
175 * will be padded with spaces by default on the left;
176 * t will be padded on the right, if the left-
177 * adjustment flag (-), described below, is given to
178 * the field width.  The field width takes the form
179 * of a decimal integer.  If the conversion character
180 * is s, the field width is the the minimum number of
181 * characters to be printed.</p>
182 *<p>
183 * An optional precision that gives the minumum number
184 * of digits to appear for the d, i, o, x or X
185 * conversions (the field is padded with leading
186 * zeros); the number of digits to appear after the
187 * radix character for the e, E, and f conversions,
188 * the maximum number of significant digits for the g
189 * and G conversions; or the maximum number of
190 * characters to be written from a string is s and S
191 * conversions.  The precision takes the form of an
192 * optional decimal digit string, where a null digit
193 * string is treated as 0.  If a precision appears
194 * with a c conversion character the precision is
195 * ignored.
196 * </p>
197 *<p>
198 * An optional h specifies that a following d, i, o,
199 * x, or X conversion character applies to a type
200 * short argument (the argument will be promoted
201 * according to the integral promotions and its value
202 * converted to type short before printing).</p>
203 *<p>
204 * An optional l (ell) specifies that a following
205 * d, i, o, x, or X conversion character applies to a
206 * type long argument.</p>
207 *<p>
208 * A field width or precision may be indicated by an
209 * asterisk (*) instead of a digit string.  In this
210 * case, an integer argument supplised the field width
211 * precision.  The argument that is actually converted
212 * is not fetched until the conversion letter is seen,
213 * so the the arguments specifying field width or
214 * precision must appear before the argument (if any)
215 * to be converted.  If the precision argument is
216 * negative, it will be changed to zero.  A negative
217 * field width argument is taken as a - flag, followed
218 * by a positive field width.</p>
219 * <p>
220 * In format strings containing the %<code>n</code>$
221 * form of a conversion specification, a field width
222 * or precision may be indicated by the sequence
223 * *<code>m</code>$, where m is a decimal integer
224 * giving the position in the argument list (after the
225 * format argument) of an integer argument containing
226 * the field width or precision.</p>
227 * <p>
228 * The format can contain either numbered argument
229 * specifications (that is, %<code>n</code>$ and
230 * *<code>m</code>$), or unnumbered argument
231 * specifications (that is % and *), but normally not
232 * both.  The only exception to this is that %% can
233 * be mixed with the %<code>n</code>$ form.  The
234 * results of mixing numbered and unnumbered argument
235 * specifications in a format string are undefined.</p>
236 *
237 *<h4>Flag Characters</h4>
238 *<p>
239 * The flags and their meanings are:</p>
240 *<dl>
241 * <dt>'<dd> integer portion of the result of a
242 *      decimal conversion (%i, %d, %f, %g, or %G) will
243 *      be formatted with thousands' grouping
244 *      characters.  For other conversions the flag
245 *      is ignored.  The non-monetary grouping
246 *      character is used.
247 * <dt>-<dd> result of the conversion is left-justified
248 *      within the field.  (It will be right-justified
249 *      if this flag is not specified).
250 * <dt>+<dd> result of a signed conversion always
251 *      begins with a sign (+ or -).  (It will begin
252 *      with a sign only when a negative value is
253 *      converted if this flag is not specified.)
254 * <dt>&lt;space&gt;<dd> If the first character of a
255 *      signed conversion is not a sign, a space
256 *      character will be placed before the result.
257 *      This means that if the space character and +
258 *      flags both appear, the space flag will be
259 *      ignored.
260 * <dt>#<dd> value is to be converted to an alternative
261 *      form.  For c, d, i, and s conversions, the flag
262 *      has no effect.  For o conversion, it increases
263 *      the precision to force the first digit of the
264 *      result to be a zero.  For x or X conversion, a
265 *      non-zero result has 0x or 0X prefixed to it,
266 *      respectively.  For e, E, f, g, and G
267 *      conversions, the result always contains a radix
268 *      character, even if no digits follow the radix
269 *      character (normally, a decimal point appears in
270 *      the result of these conversions only if a digit
271 *      follows it).  For g and G conversions, trailing
272 *      zeros will not be removed from the result as
273 *      they normally are.
274 * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G
275 *      conversions, leading zeros (following any
276 *      indication of sign or base) are used to pad to
277 *      the field width;  no space padding is
278 *      performed.  If the 0 and - flags both appear,
279 *      the 0 flag is ignored.  For d, i, o, x, and X
280 *      conversions, if a precision is specified, the
281 *      0 flag will be ignored. For c conversions,
282 *      the flag is ignored.
283 *</dl>
284 *
285 *<h4>Conversion Characters</h4>
286 *<p>
287 * Each conversion character results in fetching zero
288 * or more arguments.  The results are undefined if
289 * there are insufficient arguments for the format.
290 * Usually, an unchecked exception will be thrown.
291 * If the format is exhausted while arguments remain,
292 * the excess arguments are ignored.</p>
293 *
294 *<p>
295 * The conversion characters and their meanings are:
296 *</p>
297 *<dl>
298 * <dt>d,i<dd>The int argument is converted to a
299 *        signed decimal in the style [-]dddd.  The
300 *        precision specifies the minimum number of
301 *        digits to appear;  if the value being
302 *        converted can be represented in fewer
303 *        digits, it will be expanded with leading
304 *        zeros.  The default precision is 1.  The
305 *        result of converting 0 with an explicit
306 *        precision of 0 is no characters.
307 * <dt>o<dd> The int argument is converted to unsigned
308 *        octal format in the style ddddd.  The
309 *        precision specifies the minimum number of
310 *        digits to appear;  if the value being
311 *        converted can be represented in fewer
312 *        digits, it will be expanded with leading
313 *        zeros.  The default precision is 1.  The
314 *        result of converting 0 with an explicit
315 *        precision of 0 is no characters.
316 * <dt>x<dd> The int argument is converted to unsigned
317 *        hexadecimal format in the style dddd;  the
318 *        letters abcdef are used.  The precision
319 *        specifies the minimum numberof digits to
320 *        appear; if the value being converted can be
321 *        represented in fewer digits, it will be
322 *        expanded with leading zeros.  The default
323 *        precision is 1.  The result of converting 0
324 *        with an explicit precision of 0 is no
325 *        characters.
326 * <dt>X<dd> Behaves the same as the x conversion
327 *        character except that letters ABCDEF are
328 *        used instead of abcdef.
329 * <dt>f<dd> The floating point number argument is
330 *        written in decimal notation in the style
331 *        [-]ddd.ddd, where the number of digits after
332 *        the radix character (shown here as a decimal
333 *        point) is equal to the precision
334 *        specification.  A Locale is used to determine
335 *        the radix character to use in this format.
336 *        If the precision is omitted from the
337 *        argument, six digits are written after the
338 *        radix character;  if the precision is
339 *        explicitly 0 and the # flag is not specified,
340 *        no radix character appears.  If a radix
341 *        character appears, at least 1 digit appears
342 *        before it.  The value is rounded to the
343 *        appropriate number of digits.
344 * <dt>e,E<dd>The floating point number argument is
345 *        written in the style [-]d.ddde{+-}dd
346 *        (the symbols {+-} indicate either a plus or
347 *        minus sign), where there is one digit before
348 *        the radix character (shown here as a decimal
349 *        point) and the number of digits after it is
350 *        equal to the precision.  A Locale is used to
351 *        determine the radix character to use in this
352 *        format.  When the precision is missing, six
353 *        digits are written after the radix character;
354 *        if the precision is 0 and the # flag is not
355 *        specified, no radix character appears.  The
356 *        E conversion will produce a number with E
357 *        instead of e introducing the exponent.  The
358 *        exponent always contains at least two digits.
359 *        However, if the value to be written requires
360 *        an exponent greater than two digits,
361 *        additional exponent digits are written as
362 *        necessary.  The value is rounded to the
363 *        appropriate number of digits.
364 * <dt>g,G<dd>The floating point number argument is
365 *        written in style f or e (or in sytle E in the
366 *        case of a G conversion character), with the
367 *        precision specifying the number of
368 *        significant digits.  If the precision is
369 *        zero, it is taken as one.  The style used
370 *        depends on the value converted:  style e
371 *        (or E) will be used only if the exponent
372 *        resulting from the conversion is less than
373 *        -4 or greater than or equal to the precision.
374 *        Trailing zeros are removed from the result.
375 *        A radix character appears only if it is
376 *        followed by a digit.
377 * <dt>c,C<dd>The integer argument is converted to a
378 *        char and the result is written.
379 *
380 * <dt>s,S<dd>The argument is taken to be a string and
381 *        bytes from the string are written until the
382 *        end of the string or the number of bytes
383 *        indicated by the precision specification of
384 *        the argument is reached.  If the precision
385 *        is omitted from the argument, it is taken to
386 *        be infinite, so all characters up to the end
387 *        of the string are written.
388 * <dt>%<dd>Write a % character;  no argument is
389 *        converted.
390 *</dl>
391 *<p>
392 * If a conversion specification does not match one of
393 * the above forms, an IllegalArgumentException is
394 * thrown and the instance of PrintfFormat is not
395 * created.</p>
396 *<p>
397 * If a floating point value is the internal
398 * representation for infinity, the output is
399 * [+]Infinity, where Infinity is either Infinity or
400 * Inf, depending on the desired output string length.
401 * Printing of the sign follows the rules described
402 * above.</p>
403 *<p>
404 * If a floating point value is the internal
405 * representation for "not-a-number," the output is
406 * [+]NaN.  Printing of the sign follows the rules
407 * described above.</p>
408 *<p>
409 * In no case does a non-existent or small field width
410 * cause truncation of a field;  if the result of a
411 * conversion is wider than the field width, the field
412 * is simply expanded to contain the conversion result.
413 *</p>
414 *<p>
415 * The behavior is like printf.  One exception is that
416 * the minimum number of exponent digits is 3 instead
417 * of 2 for e and E formats when the optional L is used
418 * before the e, E, g, or G conversion character.  The
419 * optional L does not imply conversion to a long long
420 * double. </p>
421 * <p>
422 * The biggest divergence from the C printf
423 * specification is in the use of 16 bit characters.
424 * This allows the handling of characters beyond the
425 * small ASCII character set and allows the utility to
426 * interoperate correctly with the rest of the Java
427 * runtime environment.</p>
428 *<p>
429 * Omissions from the C printf specification are
430 * numerous.  All the known omissions are present
431 * because Java never uses bytes to represent
432 * characters and does not have pointers:</p>
433 *<ul>
434 * <li>%c is the same as %C.
435 * <li>%s is the same as %S.
436 * <li>u, p, and n conversion characters.
437 * <li>%ws format.
438 * <li>h modifier applied to an n conversion character.
439 * <li>l (ell) modifier applied to the c, n, or s
440 * conversion characters.
441 * <li>ll (ell ell) modifier to d, i, o, u, x, or X
442 * conversion characters.
443 * <li>ll (ell ell) modifier to an n conversion
444 * character.
445 * <li>c, C, d,i,o,u,x, and X conversion characters
446 * apply to Byte, Character, Short, Integer, Long
447 * types.
448 * <li>f, e, E, g, and G conversion characters apply
449 * to Float and Double types.
450 * <li>s and S conversion characters apply to String
451 * types.
452 * <li>All other reference types can be formatted
453 * using the s or S conversion characters only.
454 *</ul>
455 * <p>
456 * Most of this specification is quoted from the Unix
457 * man page for the sprintf utility.</p>
458 *
459 * @since 6.0.0
460 */
461public class PrintfFormat {
462
463    /**
464     *<p>
465     * ConversionSpecification allows the formatting of
466     * a single primitive or object embedded within a
467     * string.  The formatting is controlled by a
468     * format string.  Only one Java primitive or
469     * object can be formatted at a time.
470     *<p>
471     * A format string is a Java string that contains
472     * a control string.  The control string starts at
473     * the first percent sign (%) in the string,
474     * provided that this percent sign
475     *<ol>
476     *<li>is not escaped protected by a matching % or
477     *     is not an escape % character,
478     *<li>is not at the end of the format string, and
479     *<li>precedes a sequence of characters that parses
480     *     as a valid control string.
481     *</ol>
482     *<p>
483     * A control string takes the form:
484     *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
485     *                { [hlL] }+ [idfgGoxXeEcs]
486     *</pre>
487     *<p>
488     * The behavior is like printf.  One (hopefully the
489     * only) exception is that the minimum number of
490     * exponent digits is 3 instead of 2 for e and E
491     * formats when the optional L is used before the
492     * e, E, g, or G conversion character.  The
493     * optional L does not imply conversion to a long
494     * long double.
495     */
496    private final class ConversionSpecification {
497
498        /** Default precision. */
499        private static final int DEFAULT_DIGITS = 6;
500
501        /**
502         * For an o conversion, increase the precision to
503         * force the first digit of the result to be a
504         * zero.  For x (or X) conversions, a non-zero
505         * result will have 0x (or 0X) prepended to it.
506         * For e, E, f, g, or G conversions, the result
507         * will always contain a radix character, even if
508         * no digits follow the point.  For g and G
509         * conversions, trailing zeros will not be removed
510         * from the result.
511         */
512        private boolean m_alternateForm;
513
514        /** Internal variable. */
515        private int m_argumentPosition;
516
517        /** Internal variable. */
518        private int m_argumentPositionForFieldWidth;
519
520        /** Internal variable. */
521        private int m_argumentPositionForPrecision;
522
523        /** Control string type. */
524        private char m_conversionCharacter;
525
526        /**
527         * If the converted value has fewer bytes than the
528         * field width, it will be padded with spaces or
529         * zeroes.
530         */
531        private int m_fieldWidth;
532
533        /**
534         * Flag indicating whether or not the field width
535         * has been set.
536         */
537        private boolean m_fieldWidthSet;
538
539        /** Literal or control format string. */
540        private String m_fmt;
541
542        /**
543         * The result of a signed conversion will always
544         * begin with a sign (+ or -).
545         */
546        private boolean m_leadingSign;
547
548        /**
549         * Flag indicating that left padding with spaces is
550         * specified.
551         */
552        private boolean m_leadingSpace;
553
554        /**
555         * Flag indicating that left padding with zeroes is
556         * specified.
557         */
558        private boolean m_leadingZeros;
559
560        /**
561         * The result of the conversion will be
562         * left-justified within the field.
563         */
564        private boolean m_leftJustify;
565
566        /**
567         * Flag specifying that a following d, i, o, u, x,
568         * or X conversion character applies to a type
569         * short int.
570         */
571        private boolean m_optionalh;
572
573        /**
574         * Flag specifying that a following d, i, o, u, x,
575         * or X conversion character applies to a type lont
576         * int argument.
577         */
578        private boolean m_optionall;
579
580        /**
581         * Flag specifying that a following e, E, f, g, or
582         * G conversion character applies to a type double
583         * argument.  This is a noop in Java.
584         */
585        private boolean m_optionalL;
586
587        /**
588         * Position within the control string.  Used by
589         * the constructor.
590         */
591        private int m_pos;
592
593        /** Internal variable. */
594        private boolean m_positionalFieldWidth;
595
596        /** Internal variable. */
597        private boolean m_positionalPrecision;
598
599        /** Internal variable. */
600        private boolean m_positionalSpecification;
601
602        /**
603         * The minimum number of digits to appear for the
604         * d, i, o, u, x, or X conversions.  The number of
605         * digits to appear after the radix character for
606         * the e, E, and f conversions.  The maximum number
607         *  of significant digits for the g and G
608         * conversions.  The maximum number of bytes to be
609         * printed from a string in s and S conversions.
610         */
611        private int m_precision;
612
613        /**
614         * Flag indicating whether or not the precision has
615         * been set.
616         */
617        private boolean m_precisionSet;
618
619        /**
620         * The integer portion of the result of a decimal
621         * conversion (i, d, u, f, g, or G) will be
622         * formatted with thousands' grouping characters.
623         * For other conversions the flag is ignored.
624         */
625        private boolean m_thousands;
626
627        /**
628         * Flag indicating that the field width is *.
629         */
630        private boolean m_variableFieldWidth;
631
632        /**
633         * Flag indicating that the precision is *.
634         */
635        private boolean m_variablePrecision;
636
637        /**
638         * Constructor.  Used to prepare an instance
639         * to hold a literal, not a control string.
640         */
641        ConversionSpecification() {
642
643            // empty
644        }
645
646        /**
647         * Constructor for a conversion specification.
648         * The argument must begin with a % and end
649         * with the conversion character for the
650         * conversion specification.
651         * @param fmtArg  String specifying the
652         *     conversion specification.
653         * @exception CmsIllegalArgumentException if the
654         *     input string is null, zero length, or
655         *     otherwise malformed.
656         */
657        ConversionSpecification(String fmtArg)
658        throws CmsIllegalArgumentException {
659
660            if (fmtArg == null) {
661                throw new NullPointerException();
662            }
663            if (fmtArg.length() == 0) {
664                throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_CONTROL_STRING_LENGTH_0));
665            }
666            if (fmtArg.charAt(0) == '%') {
667                m_fmt = fmtArg;
668                m_pos = 1;
669                setArgPosition();
670                setFlagCharacters();
671                setFieldWidth();
672                setPrecision();
673                setOptionalHL();
674                if (setConversionCharacter()) {
675                    if (m_pos == fmtArg.length()) {
676                        if (m_leadingZeros && m_leftJustify) {
677                            m_leadingZeros = false;
678                        }
679                        if (m_precisionSet && m_leadingZeros) {
680                            if ((m_conversionCharacter == 'd')
681                                || (m_conversionCharacter == 'i')
682                                || (m_conversionCharacter == 'o')
683                                || (m_conversionCharacter == 'x')) {
684                                m_leadingZeros = false;
685                            }
686                        }
687                    } else {
688                        throw new CmsIllegalArgumentException(
689                            Messages.get().container(Messages.ERR_INVALID_CONVERSION_SPEC_1, fmtArg));
690                    }
691                } else {
692                    throw new CmsIllegalArgumentException(
693                        Messages.get().container(Messages.ERR_INVALID_CONVERSION_SPEC_1, fmtArg));
694                }
695            } else {
696                throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_CONTROL_STRING_START_0));
697            }
698        }
699
700        /**
701         * Internal helper.<p>
702         *
703         * @return the result
704         */
705        int getArgumentPosition() {
706
707            return m_argumentPosition;
708        }
709
710        /**
711         * Internal helper.<p>
712         *
713         * @return the result
714         */
715        int getArgumentPositionForFieldWidth() {
716
717            return m_argumentPositionForFieldWidth;
718        }
719
720        /**
721         * Internal helper.<p>
722         *
723         * @return the result
724         */
725        int getArgumentPositionForPrecision() {
726
727            return m_argumentPositionForPrecision;
728        }
729
730        /**
731         * Get the conversion character that tells what
732         * type of control character this instance has.
733         *
734         * @return the conversion character.
735         */
736        char getConversionCharacter() {
737
738            return m_conversionCharacter;
739        }
740
741        /**
742         * Get the String for this instance.  Translate
743         * any escape sequences.
744         *
745         * @return s the stored String.
746         */
747        String getLiteral() {
748
749            StringBuffer sb = new StringBuffer();
750            int i = 0;
751            while (i < m_fmt.length()) {
752                if (m_fmt.charAt(i) == '\\') {
753                    i++;
754                    if (i < m_fmt.length()) {
755                        char c = m_fmt.charAt(i);
756                        switch (c) {
757                            case 'a':
758                                sb.append((char)0x07);
759                                break;
760                            case 'b':
761                                sb.append('\b');
762                                break;
763                            case 'f':
764                                sb.append('\f');
765                                break;
766                            case 'n':
767                                sb.append(System.getProperty("line.separator"));
768                                break;
769                            case 'r':
770                                sb.append('\r');
771                                break;
772                            case 't':
773                                sb.append('\t');
774                                break;
775                            case 'v':
776                                sb.append((char)0x0b);
777                                break;
778                            case '\\':
779                                sb.append('\\');
780                                break;
781                            default:
782                                // noop
783                        }
784                        i++;
785                    } else {
786                        sb.append('\\');
787                    }
788                } else {
789                    i++;
790                }
791            }
792            return m_fmt;
793        }
794
795        /**
796         * Format a double argument using this conversion
797         * specification.
798         * @param s the double to format.
799         * @return the formatted String.
800         * @exception CmsIllegalArgumentException if the
801         *     conversion character is c, C, s, S, i, d,
802         *     x, X, or o.
803         */
804        String internalsprintf(double s) throws CmsIllegalArgumentException {
805
806            String s2 = "";
807            switch (m_conversionCharacter) {
808                case 'f':
809                    s2 = printFFormat(s);
810                    break;
811                case 'E':
812                case 'e':
813                    s2 = printEFormat(s);
814                    break;
815                case 'G':
816                case 'g':
817                    s2 = printGFormat(s);
818                    break;
819                default:
820                    throw new CmsIllegalArgumentException(Messages.get().container(
821                        Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
822                        "double",
823                        Character.valueOf(m_conversionCharacter)));
824            }
825            return s2;
826        }
827
828        /**
829         * Format an int argument using this conversion
830         * specification.
831         * @param s the int to format.
832         * @return the formatted String.
833         * @exception CmsIllegalArgumentException if the
834         *     conversion character is f, e, E, g, or G.
835         */
836        String internalsprintf(int s) throws CmsIllegalArgumentException {
837
838            String s2 = "";
839            switch (m_conversionCharacter) {
840                case 'd':
841                case 'i':
842                    if (m_optionalh) {
843                        s2 = printDFormat((short)s);
844                    } else if (m_optionall) {
845                        s2 = printDFormat((long)s);
846                    } else {
847                        s2 = printDFormat(s);
848                    }
849                    break;
850                case 'x':
851                case 'X':
852                    if (m_optionalh) {
853                        s2 = printXFormat((short)s);
854                    } else if (m_optionall) {
855                        s2 = printXFormat((long)s);
856                    } else {
857                        s2 = printXFormat(s);
858                    }
859                    break;
860                case 'o':
861                    if (m_optionalh) {
862                        s2 = printOFormat((short)s);
863                    } else if (m_optionall) {
864                        s2 = printOFormat((long)s);
865                    } else {
866                        s2 = printOFormat(s);
867                    }
868                    break;
869                case 'c':
870                case 'C':
871                    s2 = printCFormat((char)s);
872                    break;
873                default:
874                    throw new CmsIllegalArgumentException(Messages.get().container(
875                        Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
876                        "int",
877                        Character.valueOf(m_conversionCharacter)));
878            }
879            return s2;
880        }
881
882        /**
883         * Format a long argument using this conversion
884         * specification.
885         * @param s the long to format.
886         * @return the formatted String.
887         * @exception CmsIllegalArgumentException if the
888         *     conversion character is f, e, E, g, or G.
889         */
890        String internalsprintf(long s) throws CmsIllegalArgumentException {
891
892            String s2 = "";
893            switch (m_conversionCharacter) {
894                case 'd':
895                case 'i':
896                    if (m_optionalh) {
897                        s2 = printDFormat((short)s);
898                    } else if (m_optionall) {
899                        s2 = printDFormat(s);
900                    } else {
901                        s2 = printDFormat((int)s);
902                    }
903                    break;
904                case 'x':
905                case 'X':
906                    if (m_optionalh) {
907                        s2 = printXFormat((short)s);
908                    } else if (m_optionall) {
909                        s2 = printXFormat(s);
910                    } else {
911                        s2 = printXFormat((int)s);
912                    }
913                    break;
914                case 'o':
915                    if (m_optionalh) {
916                        s2 = printOFormat((short)s);
917                    } else if (m_optionall) {
918                        s2 = printOFormat(s);
919                    } else {
920                        s2 = printOFormat((int)s);
921                    }
922                    break;
923                case 'c':
924                case 'C':
925                    s2 = printCFormat((char)s);
926                    break;
927                default:
928                    throw new CmsIllegalArgumentException(Messages.get().container(
929                        Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
930                        "long",
931                        Character.valueOf(m_conversionCharacter)));
932            }
933            return s2;
934        }
935
936        /**
937         * Format an Object argument using this conversion
938         * specification.
939         * @param s the Object to format.
940         * @return the formatted String.
941         * @exception CmsIllegalArgumentException if the
942         *     conversion character is neither s nor S.
943         */
944        String internalsprintf(Object s) throws CmsIllegalArgumentException {
945
946            String s2 = "";
947            if ((m_conversionCharacter == 's') || (m_conversionCharacter == 'S')) {
948                s2 = printSFormat(s.toString());
949            } else {
950                throw new CmsIllegalArgumentException(
951                    Messages.get().container(
952                        Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
953                        "String",
954                        Character.valueOf(m_conversionCharacter)));
955            }
956            return s2;
957        }
958
959        /**
960         * Format a String argument using this conversion
961         * specification.
962         * @param s the String to format.
963         * @return the formatted String.
964         * @exception CmsIllegalArgumentException if the
965         *   conversion character is neither s nor S.
966         */
967        String internalsprintf(String s) throws CmsIllegalArgumentException {
968
969            String s2 = "";
970            if ((m_conversionCharacter == 's') || (m_conversionCharacter == 'S')) {
971                s2 = printSFormat(s);
972            } else {
973                throw new CmsIllegalArgumentException(Messages.get().container(
974                    Messages.ERR_INVALID_DOUBLE_FMT_CHAR_2,
975                    "String",
976                    Character.valueOf(m_conversionCharacter)));
977            }
978            return s2;
979        }
980
981        /**
982         * Internal helper.<p>
983         *
984         * @return the result
985         */
986        boolean isPositionalFieldWidth() {
987
988            return m_positionalFieldWidth;
989        }
990
991        /**
992         * Internal helper.<p>
993         *
994         * @return the result
995         */
996        boolean isPositionalPrecision() {
997
998            return m_positionalPrecision;
999        }
1000
1001        /**
1002         * Internal helper.<p>
1003         *
1004         * @return the result
1005         */
1006        boolean isPositionalSpecification() {
1007
1008            return m_positionalSpecification;
1009        }
1010
1011        /**
1012         * Check whether the specifier has a variable
1013         * field width that is going to be set by an
1014         * argument.
1015         * @return <code>true</code> if the conversion
1016         *   uses an * field width; otherwise
1017         *   <code>false</code>.
1018         */
1019        boolean isVariableFieldWidth() {
1020
1021            return m_variableFieldWidth;
1022        }
1023
1024        /**
1025         * Check whether the specifier has a variable
1026         * precision that is going to be set by an
1027         * argument.
1028         * @return <code>true</code> if the conversion
1029         *   uses an * precision; otherwise
1030         *   <code>false</code>.
1031         */
1032        boolean isVariablePrecision() {
1033
1034            return m_variablePrecision;
1035        }
1036
1037        /**
1038         * Set the field width with an argument.  A
1039         * negative field width is taken as a - flag
1040         * followed by a positive field width.
1041         * @param fw the field width.
1042         */
1043        void setFieldWidthWithArg(int fw) {
1044
1045            if (fw < 0) {
1046                m_leftJustify = true;
1047            }
1048            m_fieldWidthSet = true;
1049            m_fieldWidth = Math.abs(fw);
1050        }
1051
1052        /**
1053         * Set the String for this instance.
1054         * @param s the String to store.
1055         */
1056        void setLiteral(String s) {
1057
1058            m_fmt = s;
1059        }
1060
1061        /**
1062         * Set the precision with an argument.  A
1063         * negative precision will be changed to zero.
1064         * @param pr the precision.
1065         */
1066        void setPrecisionWithArg(int pr) {
1067
1068            m_precisionSet = true;
1069            m_precision = Math.max(pr, 0);
1070        }
1071
1072        /**
1073         * Apply zero or blank, left or right padding.
1074         * @param ca4 array of characters before padding is
1075         *     finished
1076         * @param noDigits NaN or signed Inf
1077         * @return a padded array of characters
1078         */
1079        private char[] applyFloatPadding(char[] ca4, boolean noDigits) {
1080
1081            char[] ca5 = ca4;
1082            if (m_fieldWidthSet) {
1083                int i, j, nBlanks;
1084                if (m_leftJustify) {
1085                    nBlanks = m_fieldWidth - ca4.length;
1086                    if (nBlanks > 0) {
1087                        ca5 = new char[ca4.length + nBlanks];
1088                        for (i = 0; i < ca4.length; i++) {
1089                            ca5[i] = ca4[i];
1090                        }
1091                        for (j = 0; j < nBlanks; j++, i++) {
1092                            ca5[i] = ' ';
1093                        }
1094                    }
1095                } else if (!m_leadingZeros || noDigits) {
1096                    nBlanks = m_fieldWidth - ca4.length;
1097                    if (nBlanks > 0) {
1098                        ca5 = new char[ca4.length + nBlanks];
1099                        for (i = 0; i < nBlanks; i++) {
1100                            ca5[i] = ' ';
1101                        }
1102                        for (j = 0; j < ca4.length; i++, j++) {
1103                            ca5[i] = ca4[j];
1104                        }
1105                    }
1106                } else if (m_leadingZeros) {
1107                    nBlanks = m_fieldWidth - ca4.length;
1108                    if (nBlanks > 0) {
1109                        ca5 = new char[ca4.length + nBlanks];
1110                        i = 0;
1111                        j = 0;
1112                        if (ca4[0] == '-') {
1113                            ca5[0] = '-';
1114                            i++;
1115                            j++;
1116                        }
1117                        for (int k = 0; k < nBlanks; i++, k++) {
1118                            ca5[i] = '0';
1119                        }
1120                        for (; j < ca4.length; i++, j++) {
1121                            ca5[i] = ca4[j];
1122                        }
1123                    }
1124                }
1125            }
1126            return ca5;
1127        }
1128
1129        /**
1130         * Check to see if the digits that are going to
1131         * be truncated because of the precision should
1132         * force a round in the preceding digits.
1133         * @param ca1 the array of digits
1134         * @param icarry the index of the first digit that
1135         *     is to be truncated from the print
1136         * @return <code>true</code> if the truncation forces
1137         *     a round that will change the print
1138         */
1139        private boolean checkForCarry(char[] ca1, int icarry) {
1140
1141            boolean carry = false;
1142            if (icarry < ca1.length) {
1143                if ((ca1[icarry] == '6') || (ca1[icarry] == '7') || (ca1[icarry] == '8') || (ca1[icarry] == '9')) {
1144                    carry = true;
1145                } else if (ca1[icarry] == '5') {
1146                    int ii = icarry + 1;
1147                    for (; ii < ca1.length; ii++) {
1148                        if (ca1[ii] != '0') {
1149                            break;
1150                        }
1151                    }
1152                    carry = ii < ca1.length;
1153                    if (!carry && (icarry > 0)) {
1154                        carry = ((ca1[icarry - 1] == '1')
1155                            || (ca1[icarry - 1] == '3')
1156                            || (ca1[icarry - 1] == '5')
1157                            || (ca1[icarry - 1] == '7')
1158                            || (ca1[icarry - 1] == '9'));
1159                    }
1160                }
1161            }
1162            return carry;
1163        }
1164
1165        /**
1166         * For e format, the flag character '-', means that
1167         * the output should be left justified within the
1168         * field.  The default is to pad with blanks on the
1169         * left.  '+' character means that the conversion
1170         * will always begin with a sign (+ or -).  The
1171         * blank flag character means that a non-negative
1172         * input will be preceded with a blank.  If both a
1173         * '+' and a ' ' are specified, the blank flag is
1174         * ignored.  The '0' flag character implies that
1175         * padding to the field width will be done with
1176         * zeros instead of blanks.
1177         *
1178         * The field width is treated as the minimum number
1179         * of characters to be printed.  The default is to
1180         * add no padding.  Padding is with blanks by
1181         * default.
1182         *
1183         * The precision, if set, is the minimum number of
1184         * digits to appear after the radix character.
1185         * Padding is with trailing 0s.
1186         *
1187         * The behavior is like printf.  One (hopefully the
1188         * only) exception is that the minimum number of
1189         * exponent digits is 3 instead of 2 for e and E
1190         * formats when the optional L is used before the
1191         * e, E, g, or G conversion character. The optional
1192         * L does not imply conversion to a long long
1193         * double.
1194         *
1195         * @param x the x parameter
1196         * @param eChar the eChar parameter
1197         * @return the result
1198         */
1199        private char[] eFormatDigits(double x, char eChar) {
1200
1201            char[] ca1, ca2, ca3;
1202            // int defaultDigits=6;
1203            String sx;
1204            int i, j, k, p;
1205            int expon = 0;
1206            int ePos, rPos, eSize;
1207            boolean minusSign = false;
1208            if (x > 0.0) {
1209                sx = Double.toString(x);
1210            } else if (x < 0.0) {
1211                sx = Double.toString(-x);
1212                minusSign = true;
1213            } else {
1214                sx = Double.toString(x);
1215                if (sx.charAt(0) == '-') {
1216                    minusSign = true;
1217                    sx = sx.substring(1);
1218                }
1219            }
1220            ePos = sx.indexOf('E');
1221            if (ePos == -1) {
1222                ePos = sx.indexOf('e');
1223            }
1224            rPos = sx.indexOf('.');
1225            if (ePos != -1) {
1226                int ie = ePos + 1;
1227                expon = 0;
1228                if (sx.charAt(ie) == '-') {
1229                    for (++ie; ie < sx.length(); ie++) {
1230                        if (sx.charAt(ie) != '0') {
1231                            break;
1232                        }
1233                    }
1234                    if (ie < sx.length()) {
1235                        expon = -Integer.parseInt(sx.substring(ie));
1236                    }
1237                } else {
1238                    if (sx.charAt(ie) == '+') {
1239                        ++ie;
1240                    }
1241                    for (; ie < sx.length(); ie++) {
1242                        if (sx.charAt(ie) != '0') {
1243                            break;
1244                        }
1245                    }
1246                    if (ie < sx.length()) {
1247                        expon = Integer.parseInt(sx.substring(ie));
1248                    }
1249                }
1250            }
1251            if (rPos != -1) {
1252                expon += rPos - 1;
1253            }
1254            if (m_precisionSet) {
1255                p = m_precision;
1256            } else {
1257                p = DEFAULT_DIGITS - 1;
1258            }
1259            if ((rPos != -1) && (ePos != -1)) {
1260                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
1261            } else if (rPos != -1) {
1262                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
1263            } else if (ePos != -1) {
1264                ca1 = sx.substring(0, ePos).toCharArray();
1265            } else {
1266                ca1 = sx.toCharArray();
1267            }
1268            boolean carry = false;
1269            int i0 = 0;
1270            if (ca1[0] != '0') {
1271                i0 = 0;
1272            } else {
1273                for (i0 = 0; i0 < ca1.length; i0++) {
1274                    if (ca1[i0] != '0') {
1275                        break;
1276                    }
1277                }
1278            }
1279            if ((i0 + p) < (ca1.length - 1)) {
1280                carry = checkForCarry(ca1, i0 + p + 1);
1281                if (carry) {
1282                    carry = startSymbolicCarry(ca1, i0 + p, i0);
1283                }
1284                if (carry) {
1285                    ca2 = new char[i0 + p + 1];
1286                    ca2[i0] = '1';
1287                    for (j = 0; j < i0; j++) {
1288                        ca2[j] = '0';
1289                    }
1290                    for (i = i0, j = i0 + 1; j < (p + 1); i++, j++) {
1291                        ca2[j] = ca1[i];
1292                    }
1293                    expon++;
1294                    ca1 = ca2;
1295                }
1296            }
1297            if ((Math.abs(expon) < 100) && !m_optionalL) {
1298                eSize = 4;
1299            } else {
1300                eSize = 5;
1301            }
1302            if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1303                ca2 = new char[2 + p + eSize];
1304            } else {
1305                ca2 = new char[1 + eSize];
1306            }
1307            if (ca1[0] != '0') {
1308                ca2[0] = ca1[0];
1309                j = 1;
1310            } else {
1311                for (j = 1; j < (ePos == -1 ? ca1.length : ePos); j++) {
1312                    if (ca1[j] != '0') {
1313                        break;
1314                    }
1315                }
1316                if (((ePos != -1) && (j < ePos)) || ((ePos == -1) && (j < ca1.length))) {
1317                    ca2[0] = ca1[j];
1318                    expon -= j;
1319                    j++;
1320                } else {
1321                    ca2[0] = '0';
1322                    j = 2;
1323                }
1324            }
1325            if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1326                ca2[1] = '.';
1327                i = 2;
1328            } else {
1329                i = 1;
1330            }
1331            for (k = 0; (k < p) && (j < ca1.length); j++, i++, k++) {
1332                ca2[i] = ca1[j];
1333            }
1334            for (; i < (ca2.length - eSize); i++) {
1335                ca2[i] = '0';
1336            }
1337            ca2[i++] = eChar;
1338            if (expon < 0) {
1339                ca2[i++] = '-';
1340            } else {
1341                ca2[i++] = '+';
1342            }
1343            expon = Math.abs(expon);
1344            if (expon >= 100) {
1345                switch (expon / 100) {
1346                    case 1:
1347                        ca2[i] = '1';
1348                        break;
1349                    case 2:
1350                        ca2[i] = '2';
1351                        break;
1352                    case 3:
1353                        ca2[i] = '3';
1354                        break;
1355                    case 4:
1356                        ca2[i] = '4';
1357                        break;
1358                    case 5:
1359                        ca2[i] = '5';
1360                        break;
1361                    case 6:
1362                        ca2[i] = '6';
1363                        break;
1364                    case 7:
1365                        ca2[i] = '7';
1366                        break;
1367                    case 8:
1368                        ca2[i] = '8';
1369                        break;
1370                    case 9:
1371                        ca2[i] = '9';
1372                        break;
1373                    default:
1374                        // noop
1375                }
1376                i++;
1377            }
1378            switch ((expon % 100) / 10) {
1379                case 0:
1380                    ca2[i] = '0';
1381                    break;
1382                case 1:
1383                    ca2[i] = '1';
1384                    break;
1385                case 2:
1386                    ca2[i] = '2';
1387                    break;
1388                case 3:
1389                    ca2[i] = '3';
1390                    break;
1391                case 4:
1392                    ca2[i] = '4';
1393                    break;
1394                case 5:
1395                    ca2[i] = '5';
1396                    break;
1397                case 6:
1398                    ca2[i] = '6';
1399                    break;
1400                case 7:
1401                    ca2[i] = '7';
1402                    break;
1403                case 8:
1404                    ca2[i] = '8';
1405                    break;
1406                case 9:
1407                    ca2[i] = '9';
1408                    break;
1409                default:
1410                    // noop
1411            }
1412            i++;
1413            switch (expon % 10) {
1414                case 0:
1415                    ca2[i] = '0';
1416                    break;
1417                case 1:
1418                    ca2[i] = '1';
1419                    break;
1420                case 2:
1421                    ca2[i] = '2';
1422                    break;
1423                case 3:
1424                    ca2[i] = '3';
1425                    break;
1426                case 4:
1427                    ca2[i] = '4';
1428                    break;
1429                case 5:
1430                    ca2[i] = '5';
1431                    break;
1432                case 6:
1433                    ca2[i] = '6';
1434                    break;
1435                case 7:
1436                    ca2[i] = '7';
1437                    break;
1438                case 8:
1439                    ca2[i] = '8';
1440                    break;
1441                case 9:
1442                    ca2[i] = '9';
1443                    break;
1444                default:
1445                    // noop
1446            }
1447            int nZeros = 0;
1448            if (!m_leftJustify && m_leadingZeros) {
1449                int xThousands = 0;
1450                if (m_thousands) {
1451                    int xlead = 0;
1452                    if ((ca2[0] == '+') || (ca2[0] == '-') || (ca2[0] == ' ')) {
1453                        xlead = 1;
1454                    }
1455                    int xdp = xlead;
1456                    for (; xdp < ca2.length; xdp++) {
1457                        if (ca2[xdp] == '.') {
1458                            break;
1459                        }
1460                    }
1461                    xThousands = (xdp - xlead) / 3;
1462                }
1463                if (m_fieldWidthSet) {
1464                    nZeros = m_fieldWidth - ca2.length;
1465                }
1466                if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
1467                    nZeros--;
1468                }
1469                nZeros -= xThousands;
1470                if (nZeros < 0) {
1471                    nZeros = 0;
1472                }
1473            }
1474            j = 0;
1475            if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
1476                ca3 = new char[ca2.length + nZeros + 1];
1477                j++;
1478            } else {
1479                ca3 = new char[ca2.length + nZeros];
1480            }
1481            if (!minusSign) {
1482                if (m_leadingSign) {
1483                    ca3[0] = '+';
1484                }
1485                if (m_leadingSpace) {
1486                    ca3[0] = ' ';
1487                }
1488            } else {
1489                ca3[0] = '-';
1490            }
1491            for (k = 0; k < nZeros; j++, k++) {
1492                ca3[j] = '0';
1493            }
1494            for (i = 0; (i < ca2.length) && (j < ca3.length); i++, j++) {
1495                ca3[j] = ca2[i];
1496            }
1497
1498            int lead = 0;
1499            if ((ca3[0] == '+') || (ca3[0] == '-') || (ca3[0] == ' ')) {
1500                lead = 1;
1501            }
1502            int dp = lead;
1503            for (; dp < ca3.length; dp++) {
1504                if (ca3[dp] == '.') {
1505                    break;
1506                }
1507            }
1508            int nThousands = dp / 3;
1509            // Localize the decimal point.
1510            if (dp < ca3.length) {
1511                ca3[dp] = m_dfs.getDecimalSeparator();
1512            }
1513            char[] ca4 = ca3;
1514            if (m_thousands && (nThousands > 0)) {
1515                ca4 = new char[ca3.length + nThousands + lead];
1516                ca4[0] = ca3[0];
1517                for (i = lead, k = lead; i < dp; i++) {
1518                    if ((i > 0) && (((dp - i) % 3) == 0)) {
1519                        // ca4[k]=',';
1520                        ca4[k] = m_dfs.getGroupingSeparator();
1521                        ca4[k + 1] = ca3[i];
1522                        k += 2;
1523                    } else {
1524                        ca4[k] = ca3[i];
1525                        k++;
1526                    }
1527                }
1528                for (; i < ca3.length; i++, k++) {
1529                    ca4[k] = ca3[i];
1530                }
1531            }
1532            return ca4;
1533        }
1534
1535        /**
1536         * An intermediate routine on the way to creating
1537         * an e format String.  The method decides whether
1538         * the input double value is an infinity,
1539         * not-a-number, or a finite double and formats
1540         * each type of input appropriately.
1541         * @param x the double value to be formatted.
1542         * @param eChar an 'e' or 'E' to use in the
1543         *     converted double value.
1544         * @return the converted double value.
1545         */
1546        private String eFormatString(double x, char eChar) {
1547
1548            char[] ca4, ca5;
1549            if (Double.isInfinite(x)) {
1550                if (x == Double.POSITIVE_INFINITY) {
1551                    if (m_leadingSign) {
1552                        ca4 = "+Inf".toCharArray();
1553                    } else if (m_leadingSpace) {
1554                        ca4 = " Inf".toCharArray();
1555                    } else {
1556                        ca4 = "Inf".toCharArray();
1557                    }
1558                } else {
1559                    ca4 = "-Inf".toCharArray();
1560                }
1561            } else if (Double.isNaN(x)) {
1562                if (m_leadingSign) {
1563                    ca4 = "+NaN".toCharArray();
1564                } else if (m_leadingSpace) {
1565                    ca4 = " NaN".toCharArray();
1566                } else {
1567                    ca4 = "NaN".toCharArray();
1568                }
1569            } else {
1570                ca4 = eFormatDigits(x, eChar);
1571            }
1572            ca5 = applyFloatPadding(ca4, false);
1573            return new String(ca5);
1574        }
1575
1576        /**
1577         * For f format, the flag character '-', means that
1578         * the output should be left justified within the
1579         * field.  The default is to pad with blanks on the
1580         * left.  '+' character means that the conversion
1581         * will always begin with a sign (+ or -).  The
1582         * blank flag character means that a non-negative
1583         * input will be preceded with a blank.  If both
1584         * a '+' and a ' ' are specified, the blank flag
1585         * is ignored.  The '0' flag character implies that
1586         * padding to the field width will be done with
1587         * zeros instead of blanks.
1588         *
1589         * The field width is treated as the minimum number
1590         * of characters to be printed.  The default is to
1591         * add no padding.  Padding is with blanks by
1592         * default.
1593         *
1594         * The precision, if set, is the number of digits
1595         * to appear after the radix character.  Padding is
1596         * with trailing 0s.
1597         *
1598         * @param x the paramater
1599         * @return the result
1600         */
1601        private char[] fFormatDigits(double x) {
1602
1603            // int defaultDigits=6;
1604            String sx;
1605            int i, j, k;
1606            int n1In, n2In;
1607            int expon = 0;
1608            boolean minusSign = false;
1609            if (x > 0.0) {
1610                sx = Double.toString(x);
1611            } else if (x < 0.0) {
1612                sx = Double.toString(-x);
1613                minusSign = true;
1614            } else {
1615                sx = Double.toString(x);
1616                if (sx.charAt(0) == '-') {
1617                    minusSign = true;
1618                    sx = sx.substring(1);
1619                }
1620            }
1621            int ePos = sx.indexOf('E');
1622            int rPos = sx.indexOf('.');
1623            if (rPos != -1) {
1624                n1In = rPos;
1625            } else if (ePos != -1) {
1626                n1In = ePos;
1627            } else {
1628                n1In = sx.length();
1629            }
1630            if (rPos != -1) {
1631                if (ePos != -1) {
1632                    n2In = ePos - rPos - 1;
1633                } else {
1634                    n2In = sx.length() - rPos - 1;
1635                }
1636            } else {
1637                n2In = 0;
1638            }
1639            if (ePos != -1) {
1640                int ie = ePos + 1;
1641                expon = 0;
1642                if (sx.charAt(ie) == '-') {
1643                    for (++ie; ie < sx.length(); ie++) {
1644                        if (sx.charAt(ie) != '0') {
1645                            break;
1646                        }
1647                    }
1648                    if (ie < sx.length()) {
1649                        expon = -Integer.parseInt(sx.substring(ie));
1650                    }
1651                } else {
1652                    if (sx.charAt(ie) == '+') {
1653                        ++ie;
1654                    }
1655                    for (; ie < sx.length(); ie++) {
1656                        if (sx.charAt(ie) != '0') {
1657                            break;
1658                        }
1659                    }
1660                    if (ie < sx.length()) {
1661                        expon = Integer.parseInt(sx.substring(ie));
1662                    }
1663                }
1664            }
1665            int p;
1666            if (m_precisionSet) {
1667                p = m_precision;
1668            } else {
1669                p = DEFAULT_DIGITS - 1;
1670            }
1671            char[] ca1 = sx.toCharArray();
1672            char[] ca2 = new char[n1In + n2In];
1673            char[] ca3, ca4, ca5;
1674            for (j = 0; j < n1In; j++) {
1675                ca2[j] = ca1[j];
1676            }
1677            i = j + 1;
1678            for (k = 0; k < n2In; j++, i++, k++) {
1679                ca2[j] = ca1[i];
1680            }
1681            if ((n1In + expon) <= 0) {
1682                ca3 = new char[-expon + n2In];
1683                for (j = 0, k = 0; k < (-n1In - expon); k++, j++) {
1684                    ca3[j] = '0';
1685                }
1686                for (i = 0; i < (n1In + n2In); i++, j++) {
1687                    ca3[j] = ca2[i];
1688                }
1689            } else {
1690                ca3 = ca2;
1691            }
1692            boolean carry = false;
1693            if (p < (-expon + n2In)) {
1694                if (expon < 0) {
1695                    i = p;
1696                } else {
1697                    i = p + n1In;
1698                }
1699                carry = checkForCarry(ca3, i);
1700                if (carry) {
1701                    carry = startSymbolicCarry(ca3, i - 1, 0);
1702                }
1703            }
1704            if ((n1In + expon) <= 0) {
1705                ca4 = new char[2 + p];
1706                if (!carry) {
1707                    ca4[0] = '0';
1708                } else {
1709                    ca4[0] = '1';
1710                }
1711                if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1712                    ca4[1] = '.';
1713                    for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++) {
1714                        ca4[j] = ca3[i];
1715                    }
1716                    for (; j < ca4.length; j++) {
1717                        ca4[j] = '0';
1718                    }
1719                }
1720            } else {
1721                if (!carry) {
1722                    if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1723                        ca4 = new char[n1In + expon + p + 1];
1724                    } else {
1725                        ca4 = new char[n1In + expon];
1726                    }
1727                    j = 0;
1728                } else {
1729                    if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1730                        ca4 = new char[n1In + expon + p + 2];
1731                    } else {
1732                        ca4 = new char[n1In + expon + 1];
1733                    }
1734                    ca4[0] = '1';
1735                    j = 1;
1736                }
1737                for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++) {
1738                    ca4[j] = ca3[i];
1739                }
1740                for (; i < (n1In + expon); i++, j++) {
1741                    ca4[j] = '0';
1742                }
1743                if (m_alternateForm || !m_precisionSet || (m_precision != 0)) {
1744                    ca4[j] = '.';
1745                    j++;
1746                    for (k = 0; (i < ca3.length) && (k < p); i++, j++, k++) {
1747                        ca4[j] = ca3[i];
1748                    }
1749                    for (; j < ca4.length; j++) {
1750                        ca4[j] = '0';
1751                    }
1752                }
1753            }
1754            int nZeros = 0;
1755            if (!m_leftJustify && m_leadingZeros) {
1756                int xThousands = 0;
1757                if (m_thousands) {
1758                    int xlead = 0;
1759                    if ((ca4[0] == '+') || (ca4[0] == '-') || (ca4[0] == ' ')) {
1760                        xlead = 1;
1761                    }
1762                    int xdp = xlead;
1763                    for (; xdp < ca4.length; xdp++) {
1764                        if (ca4[xdp] == '.') {
1765                            break;
1766                        }
1767                    }
1768                    xThousands = (xdp - xlead) / 3;
1769                }
1770                if (m_fieldWidthSet) {
1771                    nZeros = m_fieldWidth - ca4.length;
1772                }
1773                if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
1774                    nZeros--;
1775                }
1776                nZeros -= xThousands;
1777                if (nZeros < 0) {
1778                    nZeros = 0;
1779                }
1780            }
1781            j = 0;
1782            if ((!minusSign && (m_leadingSign || m_leadingSpace)) || minusSign) {
1783                ca5 = new char[ca4.length + nZeros + 1];
1784                j++;
1785            } else {
1786                ca5 = new char[ca4.length + nZeros];
1787            }
1788            if (!minusSign) {
1789                if (m_leadingSign) {
1790                    ca5[0] = '+';
1791                }
1792                if (m_leadingSpace) {
1793                    ca5[0] = ' ';
1794                }
1795            } else {
1796                ca5[0] = '-';
1797            }
1798            for (i = 0; i < nZeros; i++, j++) {
1799                ca5[j] = '0';
1800            }
1801            for (i = 0; i < ca4.length; i++, j++) {
1802                ca5[j] = ca4[i];
1803            }
1804            int lead = 0;
1805            if ((ca5[0] == '+') || (ca5[0] == '-') || (ca5[0] == ' ')) {
1806                lead = 1;
1807            }
1808            int dp = lead;
1809            for (; dp < ca5.length; dp++) {
1810                if (ca5[dp] == '.') {
1811                    break;
1812                }
1813            }
1814            int nThousands = (dp - lead) / 3;
1815            // Localize the decimal point.
1816            if (dp < ca5.length) {
1817                ca5[dp] = m_dfs.getDecimalSeparator();
1818            }
1819            char[] ca6 = ca5;
1820            if (m_thousands && (nThousands > 0)) {
1821                ca6 = new char[ca5.length + nThousands + lead];
1822                ca6[0] = ca5[0];
1823                for (i = lead, k = lead; i < dp; i++) {
1824                    if ((i > 0) && (((dp - i) % 3) == 0)) {
1825                        // ca6[k]=',';
1826                        ca6[k] = m_dfs.getGroupingSeparator();
1827                        ca6[k + 1] = ca5[i];
1828                        k += 2;
1829                    } else {
1830                        ca6[k] = ca5[i];
1831                        k++;
1832                    }
1833                }
1834                for (; i < ca5.length; i++, k++) {
1835                    ca6[k] = ca5[i];
1836                }
1837            }
1838            return ca6;
1839        }
1840
1841        /**
1842         * An intermediate routine on the way to creating
1843         * an f format String.  The method decides whether
1844         * the input double value is an infinity,
1845         * not-a-number, or a finite double and formats
1846         * each type of input appropriately.
1847         * @param x the double value to be formatted.
1848         * @return the converted double value.
1849         */
1850        private String fFormatString(double x) {
1851
1852            char[] ca6, ca7;
1853            if (Double.isInfinite(x)) {
1854                if (x == Double.POSITIVE_INFINITY) {
1855                    if (m_leadingSign) {
1856                        ca6 = "+Inf".toCharArray();
1857                    } else if (m_leadingSpace) {
1858                        ca6 = " Inf".toCharArray();
1859                    } else {
1860                        ca6 = "Inf".toCharArray();
1861                    }
1862                } else {
1863                    ca6 = "-Inf".toCharArray();
1864                }
1865            } else if (Double.isNaN(x)) {
1866                if (m_leadingSign) {
1867                    ca6 = "+NaN".toCharArray();
1868                } else if (m_leadingSpace) {
1869                    ca6 = " NaN".toCharArray();
1870                } else {
1871                    ca6 = "NaN".toCharArray();
1872                }
1873            } else {
1874                ca6 = fFormatDigits(x);
1875            }
1876            ca7 = applyFloatPadding(ca6, false);
1877            return new String(ca7);
1878        }
1879
1880        /**
1881         * Format method for the c conversion character and
1882         * char argument.
1883         *
1884         * The only flag character that affects c format is
1885         * the '-', meaning that the output should be left
1886         * justified within the field.  The default is to
1887         * pad with blanks on the left.
1888         *
1889         * The field width is treated as the minimum number
1890         * of characters to be printed.  Padding is with
1891         * blanks by default.  The default width is 1.
1892         *
1893         * The precision, if set, is ignored.
1894         * @param x the char to format.
1895         * @return the formatted String.
1896         */
1897        private String printCFormat(char x) {
1898
1899            int nPrint = 1;
1900            int width = m_fieldWidth;
1901            if (!m_fieldWidthSet) {
1902                width = nPrint;
1903            }
1904            char[] ca = new char[width];
1905            int i = 0;
1906            if (m_leftJustify) {
1907                ca[0] = x;
1908                for (i = 1; i <= (width - nPrint); i++) {
1909                    ca[i] = ' ';
1910                }
1911            } else {
1912                for (i = 0; i < (width - nPrint); i++) {
1913                    ca[i] = ' ';
1914                }
1915                ca[i] = x;
1916            }
1917            return new String(ca);
1918        }
1919
1920        /**
1921         * Format method for the d conversion character and
1922         * int argument.
1923         *
1924         * For d format, the flag character '-', means that
1925         * the output should be left justified within the
1926         * field.  The default is to pad with blanks on the
1927         * left.  A '+' character means that the conversion
1928         * will always begin with a sign (+ or -).  The
1929         * blank flag character means that a non-negative
1930         * input will be preceded with a blank.  If both a
1931         * '+' and a ' ' are specified, the blank flag is
1932         * ignored.  The '0' flag character implies that
1933         * padding to the field width will be done with
1934         * zeros instead of blanks.
1935         *
1936         * The field width is treated as the minimum number
1937         * of characters to be printed.  The default is to
1938         * add no padding.  Padding is with blanks by
1939         * default.
1940         *
1941         * The precision, if set, is the minimum number of
1942         * digits to appear.  Padding is with leading 0s.
1943         * @param x the int to format.
1944         * @return the formatted String.
1945         */
1946        private String printDFormat(int x) {
1947
1948            return printDFormat(Integer.toString(x));
1949        }
1950
1951        /**
1952         * Format method for the d conversion character and
1953         * long argument.
1954         *
1955         * For d format, the flag character '-', means that
1956         * the output should be left justified within the
1957         * field.  The default is to pad with blanks on the
1958         * left.  A '+' character means that the conversion
1959         * will always begin with a sign (+ or -).  The
1960         * blank flag character means that a non-negative
1961         * input will be preceded with a blank.  If both a
1962         * '+' and a ' ' are specified, the blank flag is
1963         * ignored.  The '0' flag character implies that
1964         * padding to the field width will be done with
1965         * zeros instead of blanks.
1966         *
1967         * The field width is treated as the minimum number
1968         * of characters to be printed.  The default is to
1969         * add no padding.  Padding is with blanks by
1970         * default.
1971         *
1972         * The precision, if set, is the minimum number of
1973         * digits to appear.  Padding is with leading 0s.
1974         * @param x the long to format.
1975         * @return the formatted String.
1976         */
1977        private String printDFormat(long x) {
1978
1979            return printDFormat(Long.toString(x));
1980        }
1981
1982        /**
1983         * Format method for the d conversion specifer and
1984         * short argument.
1985         *
1986         * For d format, the flag character '-', means that
1987         * the output should be left justified within the
1988         * field.  The default is to pad with blanks on the
1989         * left.  A '+' character means that the conversion
1990         * will always begin with a sign (+ or -).  The
1991         * blank flag character means that a non-negative
1992         * input will be preceded with a blank.  If both a
1993         * '+' and a ' ' are specified, the blank flag is
1994         * ignored.  The '0' flag character implies that
1995         * padding to the field width will be done with
1996         * zeros instead of blanks.
1997         *
1998         * The field width is treated as the minimum number
1999         * of characters to be printed.  The default is to
2000         * add no padding.  Padding is with blanks by
2001         * default.
2002         *
2003         * The precision, if set, is the minimum number of
2004         * digits to appear.  Padding is with leading 0s.
2005         * @param x the short to format.
2006         * @return the formatted String.
2007         */
2008        private String printDFormat(short x) {
2009
2010            return printDFormat(Short.toString(x));
2011        }
2012
2013        /**
2014         * Utility method for formatting using the d
2015         * conversion character.
2016         * @param sx the String to format, the result of
2017         *     converting a short, int, or long to a
2018         *     String.
2019         * @return the formatted String.
2020         */
2021        private String printDFormat(String sx) {
2022
2023            int nLeadingZeros = 0;
2024            int nBlanks = 0, n = 0;
2025            int i = 0, jFirst = 0;
2026            boolean neg = sx.charAt(0) == '-';
2027            if (sx.equals("0") && m_precisionSet && (m_precision == 0)) {
2028                sx = "";
2029            }
2030            if (!neg) {
2031                if (m_precisionSet && (sx.length() < m_precision)) {
2032                    nLeadingZeros = m_precision - sx.length();
2033                }
2034            } else {
2035                if (m_precisionSet && ((sx.length() - 1) < m_precision)) {
2036                    nLeadingZeros = (m_precision - sx.length()) + 1;
2037                }
2038            }
2039            if (nLeadingZeros < 0) {
2040                nLeadingZeros = 0;
2041            }
2042            if (m_fieldWidthSet) {
2043                nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
2044                if (!neg && (m_leadingSign || m_leadingSpace)) {
2045                    nBlanks--;
2046                }
2047            }
2048            if (nBlanks < 0) {
2049                nBlanks = 0;
2050            }
2051            if (m_leadingSign) {
2052                n++;
2053            } else if (m_leadingSpace) {
2054                n++;
2055            }
2056            n += nBlanks;
2057            n += nLeadingZeros;
2058            n += sx.length();
2059            char[] ca = new char[n];
2060            if (m_leftJustify) {
2061                if (neg) {
2062                    ca[i++] = '-';
2063                } else if (m_leadingSign) {
2064                    ca[i++] = '+';
2065                } else if (m_leadingSpace) {
2066                    ca[i++] = ' ';
2067                }
2068                char[] csx = sx.toCharArray();
2069                jFirst = neg ? 1 : 0;
2070                for (int j = 0; j < nLeadingZeros; i++, j++) {
2071                    ca[i] = '0';
2072                }
2073                for (int j = jFirst; j < csx.length; j++, i++) {
2074                    ca[i] = csx[j];
2075                }
2076                for (int j = 0; j < nBlanks; i++, j++) {
2077                    ca[i] = ' ';
2078                }
2079            } else {
2080                if (!m_leadingZeros) {
2081                    for (i = 0; i < nBlanks; i++) {
2082                        ca[i] = ' ';
2083                    }
2084                    if (neg) {
2085                        ca[i++] = '-';
2086                    } else if (m_leadingSign) {
2087                        ca[i++] = '+';
2088                    } else if (m_leadingSpace) {
2089                        ca[i++] = ' ';
2090                    }
2091                } else {
2092                    if (neg) {
2093                        ca[i++] = '-';
2094                    } else if (m_leadingSign) {
2095                        ca[i++] = '+';
2096                    } else if (m_leadingSpace) {
2097                        ca[i++] = ' ';
2098                    }
2099                    for (int j = 0; j < nBlanks; j++, i++) {
2100                        ca[i] = '0';
2101                    }
2102                }
2103                for (int j = 0; j < nLeadingZeros; j++, i++) {
2104                    ca[i] = '0';
2105                }
2106                char[] csx = sx.toCharArray();
2107                jFirst = neg ? 1 : 0;
2108                for (int j = jFirst; j < csx.length; j++, i++) {
2109                    ca[i] = csx[j];
2110                }
2111            }
2112            return new String(ca);
2113        }
2114
2115        /**
2116         * Format method for the e or E conversion
2117         * character.
2118         * @param x the double to format.
2119         * @return the formatted String.
2120         */
2121        private String printEFormat(double x) {
2122
2123            if (m_conversionCharacter == 'e') {
2124                return eFormatString(x, 'e');
2125            } else {
2126                return eFormatString(x, 'E');
2127            }
2128        }
2129
2130        /**
2131         * Format method for the f conversion character.
2132         * @param x the double to format.
2133         * @return the formatted String.
2134         */
2135        private String printFFormat(double x) {
2136
2137            return fFormatString(x);
2138        }
2139
2140        /**
2141         * Format method for the g conversion character.
2142         *
2143         * For g format, the flag character '-', means that
2144         *  the output should be left justified within the
2145         * field.  The default is to pad with blanks on the
2146         * left.  '+' character means that the conversion
2147         * will always begin with a sign (+ or -).  The
2148         * blank flag character means that a non-negative
2149         * input will be preceded with a blank.  If both a
2150         * '+' and a ' ' are specified, the blank flag is
2151         * ignored.  The '0' flag character implies that
2152         * padding to the field width will be done with
2153         * zeros instead of blanks.
2154         *
2155         * The field width is treated as the minimum number
2156         * of characters to be printed.  The default is to
2157         * add no padding.  Padding is with blanks by
2158         * default.
2159         *
2160         * The precision, if set, is the minimum number of
2161         * digits to appear after the radix character.
2162         * Padding is with trailing 0s.
2163         * @param x the double to format.
2164         * @return the formatted String.
2165         */
2166        private String printGFormat(double x) {
2167
2168            String sx, sy, sz, ret;
2169            int savePrecision = m_precision;
2170            int i;
2171            char[] ca4, ca5;
2172            if (Double.isInfinite(x)) {
2173                if (x == Double.POSITIVE_INFINITY) {
2174                    if (m_leadingSign) {
2175                        ca4 = "+Inf".toCharArray();
2176                    } else if (m_leadingSpace) {
2177                        ca4 = " Inf".toCharArray();
2178                    } else {
2179                        ca4 = "Inf".toCharArray();
2180                    }
2181                } else {
2182                    ca4 = "-Inf".toCharArray();
2183                }
2184            } else if (Double.isNaN(x)) {
2185                if (m_leadingSign) {
2186                    ca4 = "+NaN".toCharArray();
2187                } else if (m_leadingSpace) {
2188                    ca4 = " NaN".toCharArray();
2189                } else {
2190                    ca4 = "NaN".toCharArray();
2191                }
2192            } else {
2193                if (!m_precisionSet) {
2194                    m_precision = DEFAULT_DIGITS;
2195                }
2196                if (m_precision == 0) {
2197                    m_precision = 1;
2198                }
2199                int ePos = -1;
2200                if (m_conversionCharacter == 'g') {
2201                    sx = eFormatString(x, 'e').trim();
2202                    ePos = sx.indexOf('e');
2203                } else {
2204                    sx = eFormatString(x, 'E').trim();
2205                    ePos = sx.indexOf('E');
2206                }
2207                i = ePos + 1;
2208                int expon = 0;
2209                if (sx.charAt(i) == '-') {
2210                    for (++i; i < sx.length(); i++) {
2211                        if (sx.charAt(i) != '0') {
2212                            break;
2213                        }
2214                    }
2215                    if (i < sx.length()) {
2216                        expon = -Integer.parseInt(sx.substring(i));
2217                    }
2218                } else {
2219                    if (sx.charAt(i) == '+') {
2220                        ++i;
2221                    }
2222                    for (; i < sx.length(); i++) {
2223                        if (sx.charAt(i) != '0') {
2224                            break;
2225                        }
2226                    }
2227                    if (i < sx.length()) {
2228                        expon = Integer.parseInt(sx.substring(i));
2229                    }
2230                }
2231                // Trim trailing zeros.
2232                // If the radix character is not followed by
2233                // a digit, trim it, too.
2234                if (!m_alternateForm) {
2235                    if ((expon >= -4) && (expon < m_precision)) {
2236                        sy = fFormatString(x).trim();
2237                    } else {
2238                        sy = sx.substring(0, ePos);
2239                    }
2240                    i = sy.length() - 1;
2241                    for (; i >= 0; i--) {
2242                        if (sy.charAt(i) != '0') {
2243                            break;
2244                        }
2245                    }
2246                    if ((i >= 0) && (sy.charAt(i) == '.')) {
2247                        i--;
2248                    }
2249                    if (i == -1) {
2250                        sz = "0";
2251                    } else if (!Character.isDigit(sy.charAt(i))) {
2252                        sz = sy.substring(0, i + 1) + "0";
2253                    } else {
2254                        sz = sy.substring(0, i + 1);
2255                    }
2256                    if ((expon >= -4) && (expon < m_precision)) {
2257                        ret = sz;
2258                    } else {
2259                        ret = sz + sx.substring(ePos);
2260                    }
2261                } else {
2262                    if ((expon >= -4) && (expon < m_precision)) {
2263                        ret = fFormatString(x).trim();
2264                    } else {
2265                        ret = sx;
2266                    }
2267                }
2268                // leading space was trimmed off during
2269                // construction
2270                if (m_leadingSpace) {
2271                    if (x >= 0) {
2272                        ret = " " + ret;
2273                    }
2274                }
2275                ca4 = ret.toCharArray();
2276            }
2277            // Pad with blanks or zeros.
2278            ca5 = applyFloatPadding(ca4, false);
2279            m_precision = savePrecision;
2280            return new String(ca5);
2281        }
2282
2283        /**
2284         * Format method for the o conversion character and
2285         * int argument.
2286         *
2287         * For o format, the flag character '-', means that
2288         * the output should be left justified within the
2289         * field.  The default is to pad with blanks on the
2290         * left.  The '#' flag character means that the
2291         * output begins with a leading 0 and the precision
2292         * is increased by 1.
2293         *
2294         * The field width is treated as the minimum number
2295         * of characters to be printed.  The default is to
2296         * add no padding.  Padding is with blanks by
2297         * default.
2298         *
2299         * The precision, if set, is the minimum number of
2300         * digits to appear.  Padding is with leading 0s.
2301         * @param x the int to format.
2302         * @return the formatted String.
2303         */
2304        private String printOFormat(int x) {
2305
2306            String sx = null;
2307            if (x == Integer.MIN_VALUE) {
2308                sx = "20000000000";
2309            } else if (x < 0) {
2310                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
2311                switch (t.length()) {
2312                    case 1:
2313                        sx = "2000000000" + t;
2314                        break;
2315                    case 2:
2316                        sx = "200000000" + t;
2317                        break;
2318                    case 3:
2319                        sx = "20000000" + t;
2320                        break;
2321                    case 4:
2322                        sx = "2000000" + t;
2323                        break;
2324                    case 5:
2325                        sx = "200000" + t;
2326                        break;
2327                    case 6:
2328                        sx = "20000" + t;
2329                        break;
2330                    case 7:
2331                        sx = "2000" + t;
2332                        break;
2333                    case 8:
2334                        sx = "200" + t;
2335                        break;
2336                    case 9:
2337                        sx = "20" + t;
2338                        break;
2339                    case 10:
2340                        sx = "2" + t;
2341                        break;
2342                    case 11:
2343                        sx = "3" + t.substring(1);
2344                        break;
2345                    default:
2346                        // noop
2347                }
2348            } else {
2349                sx = Integer.toString(x, 8);
2350            }
2351            return printOFormat(sx);
2352        }
2353
2354        /**
2355         * Format method for the o conversion character and
2356         * long argument.
2357         *
2358         * For o format, the flag character '-', means that
2359         * the output should be left justified within the
2360         * field.  The default is to pad with blanks on the
2361         * left.  The '#' flag character means that the
2362         * output begins with a leading 0 and the precision
2363         * is increased by 1.
2364         *
2365         * The field width is treated as the minimum number
2366         * of characters to be printed.  The default is to
2367         * add no padding.  Padding is with blanks by
2368         * default.
2369         *
2370         * The precision, if set, is the minimum number of
2371         * digits to appear.  Padding is with leading 0s.
2372         * @param x the long to format.
2373         * @return the formatted String.
2374         */
2375        private String printOFormat(long x) {
2376
2377            String sx = null;
2378            if (x == Long.MIN_VALUE) {
2379                sx = "1000000000000000000000";
2380            } else if (x < 0) {
2381                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
2382                switch (t.length()) {
2383                    case 1:
2384                        sx = "100000000000000000000" + t;
2385                        break;
2386                    case 2:
2387                        sx = "10000000000000000000" + t;
2388                        break;
2389                    case 3:
2390                        sx = "1000000000000000000" + t;
2391                        break;
2392                    case 4:
2393                        sx = "100000000000000000" + t;
2394                        break;
2395                    case 5:
2396                        sx = "10000000000000000" + t;
2397                        break;
2398                    case 6:
2399                        sx = "1000000000000000" + t;
2400                        break;
2401                    case 7:
2402                        sx = "100000000000000" + t;
2403                        break;
2404                    case 8:
2405                        sx = "10000000000000" + t;
2406                        break;
2407                    case 9:
2408                        sx = "1000000000000" + t;
2409                        break;
2410                    case 10:
2411                        sx = "100000000000" + t;
2412                        break;
2413                    case 11:
2414                        sx = "10000000000" + t;
2415                        break;
2416                    case 12:
2417                        sx = "1000000000" + t;
2418                        break;
2419                    case 13:
2420                        sx = "100000000" + t;
2421                        break;
2422                    case 14:
2423                        sx = "10000000" + t;
2424                        break;
2425                    case 15:
2426                        sx = "1000000" + t;
2427                        break;
2428                    case 16:
2429                        sx = "100000" + t;
2430                        break;
2431                    case 17:
2432                        sx = "10000" + t;
2433                        break;
2434                    case 18:
2435                        sx = "1000" + t;
2436                        break;
2437                    case 19:
2438                        sx = "100" + t;
2439                        break;
2440                    case 20:
2441                        sx = "10" + t;
2442                        break;
2443                    case 21:
2444                        sx = "1" + t;
2445                        break;
2446                    default:
2447                        // noop
2448                }
2449            } else {
2450                sx = Long.toString(x, 8);
2451            }
2452            return printOFormat(sx);
2453        }
2454
2455        /**
2456         * Format method for the o conversion character and
2457         * short argument.
2458         *
2459         * For o format, the flag character '-', means that
2460         * the output should be left justified within the
2461         * field.  The default is to pad with blanks on the
2462         * left.  The '#' flag character means that the
2463         * output begins with a leading 0 and the precision
2464         * is increased by 1.
2465         *
2466         * The field width is treated as the minimum number
2467         * of characters to be printed.  The default is to
2468         * add no padding.  Padding is with blanks by
2469         * default.
2470         *
2471         * The precision, if set, is the minimum number of
2472         * digits to appear.  Padding is with leading 0s.
2473         * @param x the short to format.
2474         * @return the formatted String.
2475         */
2476        private String printOFormat(short x) {
2477
2478            String sx = null;
2479            if (x == Short.MIN_VALUE) {
2480                sx = "100000";
2481            } else if (x < 0) {
2482                String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
2483                switch (t.length()) {
2484                    case 1:
2485                        sx = "10000" + t;
2486                        break;
2487                    case 2:
2488                        sx = "1000" + t;
2489                        break;
2490                    case 3:
2491                        sx = "100" + t;
2492                        break;
2493                    case 4:
2494                        sx = "10" + t;
2495                        break;
2496                    case 5:
2497                        sx = "1" + t;
2498                        break;
2499                    default:
2500                        // noop
2501                }
2502            } else {
2503                sx = Integer.toString(x, 8);
2504            }
2505            return printOFormat(sx);
2506        }
2507
2508        /**
2509         * Utility method for formatting using the o
2510         * conversion character.
2511         * @param sx the String to format, the result of
2512         *     converting a short, int, or long to a
2513         *     String.
2514         * @return the formatted String.
2515         */
2516        private String printOFormat(String sx) {
2517
2518            int nLeadingZeros = 0;
2519            int nBlanks = 0;
2520            if (sx.equals("0") && m_precisionSet && (m_precision == 0)) {
2521                sx = "";
2522            }
2523            if (m_precisionSet) {
2524                nLeadingZeros = m_precision - sx.length();
2525            }
2526            if (m_alternateForm) {
2527                nLeadingZeros++;
2528            }
2529            if (nLeadingZeros < 0) {
2530                nLeadingZeros = 0;
2531            }
2532            if (m_fieldWidthSet) {
2533                nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
2534            }
2535            if (nBlanks < 0) {
2536                nBlanks = 0;
2537            }
2538            int n = nLeadingZeros + sx.length() + nBlanks;
2539            char[] ca = new char[n];
2540            int i;
2541            if (m_leftJustify) {
2542                for (i = 0; i < nLeadingZeros; i++) {
2543                    ca[i] = '0';
2544                }
2545                char[] csx = sx.toCharArray();
2546                for (int j = 0; j < csx.length; j++, i++) {
2547                    ca[i] = csx[j];
2548                }
2549                for (int j = 0; j < nBlanks; j++, i++) {
2550                    ca[i] = ' ';
2551                }
2552            } else {
2553                if (m_leadingZeros) {
2554                    for (i = 0; i < nBlanks; i++) {
2555                        ca[i] = '0';
2556                    }
2557                } else {
2558                    for (i = 0; i < nBlanks; i++) {
2559                        ca[i] = ' ';
2560                    }
2561                }
2562                for (int j = 0; j < nLeadingZeros; j++, i++) {
2563                    ca[i] = '0';
2564                }
2565                char[] csx = sx.toCharArray();
2566                for (int j = 0; j < csx.length; j++, i++) {
2567                    ca[i] = csx[j];
2568                }
2569            }
2570            return new String(ca);
2571        }
2572
2573        /**
2574         * Format method for the s conversion character and
2575         * String argument.
2576         *
2577         * The only flag character that affects s format is
2578         * the '-', meaning that the output should be left
2579         * justified within the field.  The default is to
2580         * pad with blanks on the left.
2581         *
2582         * The field width is treated as the minimum number
2583         * of characters to be printed.  The default is the
2584         * smaller of the number of characters in the the
2585         * input and the precision.  Padding is with blanks
2586         * by default.
2587         *
2588         * The precision, if set, specifies the maximum
2589         * number of characters to be printed from the
2590         * string.  A null digit string is treated
2591         * as a 0.  The default is not to set a maximum
2592         * number of characters to be printed.
2593         * @param x the String to format.
2594         * @return the formatted String.
2595         */
2596        private String printSFormat(String x) {
2597
2598            int nPrint = x.length();
2599            int width = m_fieldWidth;
2600            if (m_precisionSet && (nPrint > m_precision)) {
2601                nPrint = m_precision;
2602            }
2603            if (!m_fieldWidthSet) {
2604                width = nPrint;
2605            }
2606            int n = 0;
2607            if (width > nPrint) {
2608                n += width - nPrint;
2609            }
2610            if (nPrint >= x.length()) {
2611                n += x.length();
2612            } else {
2613                n += nPrint;
2614            }
2615            char[] ca = new char[n];
2616            int i = 0;
2617            if (m_leftJustify) {
2618                if (nPrint >= x.length()) {
2619                    char[] csx = x.toCharArray();
2620                    for (i = 0; i < x.length(); i++) {
2621                        ca[i] = csx[i];
2622                    }
2623                } else {
2624                    char[] csx = x.substring(0, nPrint).toCharArray();
2625                    for (i = 0; i < nPrint; i++) {
2626                        ca[i] = csx[i];
2627                    }
2628                }
2629                for (int j = 0; j < (width - nPrint); j++, i++) {
2630                    ca[i] = ' ';
2631                }
2632            } else {
2633                for (i = 0; i < (width - nPrint); i++) {
2634                    ca[i] = ' ';
2635                }
2636                if (nPrint >= x.length()) {
2637                    char[] csx = x.toCharArray();
2638                    for (int j = 0; j < x.length(); i++, j++) {
2639                        ca[i] = csx[j];
2640                    }
2641                } else {
2642                    char[] csx = x.substring(0, nPrint).toCharArray();
2643                    for (int j = 0; j < nPrint; i++, j++) {
2644                        ca[i] = csx[j];
2645                    }
2646                }
2647            }
2648            return new String(ca);
2649        }
2650
2651        /**
2652         * Format method for the x conversion character and
2653         * int argument.
2654         *
2655         * For x format, the flag character '-', means that
2656         * the output should be left justified within the
2657         * field.  The default is to pad with blanks on the
2658         * left.  The '#' flag character means to lead with
2659         * '0x'.
2660         *
2661         * The field width is treated as the minimum number
2662         * of characters to be printed.  The default is to
2663         * add no padding.  Padding is with blanks by
2664         * default.
2665         *
2666         * The precision, if set, is the minimum number of
2667         * digits to appear.  Padding is with leading 0s.
2668         * @param x the int to format.
2669         * @return the formatted String.
2670         */
2671        private String printXFormat(int x) {
2672
2673            String sx = null;
2674            if (x == Integer.MIN_VALUE) {
2675                sx = "80000000";
2676            } else if (x < 0) {
2677                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
2678                switch (t.length()) {
2679                    case 1:
2680                        sx = "8000000" + t;
2681                        break;
2682                    case 2:
2683                        sx = "800000" + t;
2684                        break;
2685                    case 3:
2686                        sx = "80000" + t;
2687                        break;
2688                    case 4:
2689                        sx = "8000" + t;
2690                        break;
2691                    case 5:
2692                        sx = "800" + t;
2693                        break;
2694                    case 6:
2695                        sx = "80" + t;
2696                        break;
2697                    case 7:
2698                        sx = "8" + t;
2699                        break;
2700                    case 8:
2701                        switch (t.charAt(0)) {
2702                            case '1':
2703                                sx = "9" + t.substring(1, 8);
2704                                break;
2705                            case '2':
2706                                sx = "a" + t.substring(1, 8);
2707                                break;
2708                            case '3':
2709                                sx = "b" + t.substring(1, 8);
2710                                break;
2711                            case '4':
2712                                sx = "c" + t.substring(1, 8);
2713                                break;
2714                            case '5':
2715                                sx = "d" + t.substring(1, 8);
2716                                break;
2717                            case '6':
2718                                sx = "e" + t.substring(1, 8);
2719                                break;
2720                            case '7':
2721                                sx = "f" + t.substring(1, 8);
2722                                break;
2723                            default:
2724                                // noop
2725                        }
2726                        break;
2727                    default:
2728                        // noop
2729                }
2730            } else {
2731                sx = Integer.toString(x, 16);
2732            }
2733            return printXFormat(sx);
2734        }
2735
2736        /**
2737         * Format method for the x conversion character and
2738         * long argument.
2739         *
2740         * For x format, the flag character '-', means that
2741         * the output should be left justified within the
2742         * field.  The default is to pad with blanks on the
2743         * left.  The '#' flag character means to lead with
2744         * '0x'.
2745         *
2746         * The field width is treated as the minimum number
2747         * of characters to be printed.  The default is to
2748         * add no padding.  Padding is with blanks by
2749         * default.
2750         *
2751         * The precision, if set, is the minimum number of
2752         * digits to appear.  Padding is with leading 0s.
2753         * @param x the long to format.
2754         * @return the formatted String.
2755         */
2756        private String printXFormat(long x) {
2757
2758            String sx = null;
2759            if (x == Long.MIN_VALUE) {
2760                sx = "8000000000000000";
2761            } else if (x < 0) {
2762                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
2763                switch (t.length()) {
2764                    case 1:
2765                        sx = "800000000000000" + t;
2766                        break;
2767                    case 2:
2768                        sx = "80000000000000" + t;
2769                        break;
2770                    case 3:
2771                        sx = "8000000000000" + t;
2772                        break;
2773                    case 4:
2774                        sx = "800000000000" + t;
2775                        break;
2776                    case 5:
2777                        sx = "80000000000" + t;
2778                        break;
2779                    case 6:
2780                        sx = "8000000000" + t;
2781                        break;
2782                    case 7:
2783                        sx = "800000000" + t;
2784                        break;
2785                    case 8:
2786                        sx = "80000000" + t;
2787                        break;
2788                    case 9:
2789                        sx = "8000000" + t;
2790                        break;
2791                    case 10:
2792                        sx = "800000" + t;
2793                        break;
2794                    case 11:
2795                        sx = "80000" + t;
2796                        break;
2797                    case 12:
2798                        sx = "8000" + t;
2799                        break;
2800                    case 13:
2801                        sx = "800" + t;
2802                        break;
2803                    case 14:
2804                        sx = "80" + t;
2805                        break;
2806                    case 15:
2807                        sx = "8" + t;
2808                        break;
2809                    case 16:
2810                        switch (t.charAt(0)) {
2811                            case '1':
2812                                sx = "9" + t.substring(1, 16);
2813                                break;
2814                            case '2':
2815                                sx = "a" + t.substring(1, 16);
2816                                break;
2817                            case '3':
2818                                sx = "b" + t.substring(1, 16);
2819                                break;
2820                            case '4':
2821                                sx = "c" + t.substring(1, 16);
2822                                break;
2823                            case '5':
2824                                sx = "d" + t.substring(1, 16);
2825                                break;
2826                            case '6':
2827                                sx = "e" + t.substring(1, 16);
2828                                break;
2829                            case '7':
2830                                sx = "f" + t.substring(1, 16);
2831                                break;
2832                            default:
2833                                // noop
2834                        }
2835                        break;
2836                    default:
2837                        // noop
2838                }
2839            } else {
2840                sx = Long.toString(x, 16);
2841            }
2842            return printXFormat(sx);
2843        }
2844
2845        /**
2846         * Format method for the x conversion character and
2847         * short argument.
2848         *
2849         * For x format, the flag character '-', means that
2850         * the output should be left justified within the
2851         * field.  The default is to pad with blanks on the
2852         * left.  The '#' flag character means to lead with
2853         * '0x'.
2854         *
2855         * The field width is treated as the minimum number
2856         * of characters to be printed.  The default is to
2857         * add no padding.  Padding is with blanks by
2858         * default.
2859         *
2860         * The precision, if set, is the minimum number of
2861         * digits to appear.  Padding is with leading 0s.
2862         * @param x the short to format.
2863         * @return the formatted String.
2864         */
2865        private String printXFormat(short x) {
2866
2867            String sx = null;
2868            if (x == Short.MIN_VALUE) {
2869                sx = "8000";
2870            } else if (x < 0) {
2871                String t;
2872                if (x == Short.MIN_VALUE) {
2873                    t = "0";
2874                } else {
2875                    t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
2876                    if ((t.charAt(0) == 'F') || (t.charAt(0) == 'f')) {
2877                        t = t.substring(16, 32);
2878                    }
2879                }
2880                switch (t.length()) {
2881                    case 1:
2882                        sx = "800" + t;
2883                        break;
2884                    case 2:
2885                        sx = "80" + t;
2886                        break;
2887                    case 3:
2888                        sx = "8" + t;
2889                        break;
2890                    case 4:
2891                        switch (t.charAt(0)) {
2892                            case '1':
2893                                sx = "9" + t.substring(1, 4);
2894                                break;
2895                            case '2':
2896                                sx = "a" + t.substring(1, 4);
2897                                break;
2898                            case '3':
2899                                sx = "b" + t.substring(1, 4);
2900                                break;
2901                            case '4':
2902                                sx = "c" + t.substring(1, 4);
2903                                break;
2904                            case '5':
2905                                sx = "d" + t.substring(1, 4);
2906                                break;
2907                            case '6':
2908                                sx = "e" + t.substring(1, 4);
2909                                break;
2910                            case '7':
2911                                sx = "f" + t.substring(1, 4);
2912                                break;
2913                            default:
2914                                // noop
2915                        }
2916                        break;
2917                    default:
2918                        // noop
2919                }
2920            } else {
2921                sx = Integer.toString(x, 16);
2922            }
2923            return printXFormat(sx);
2924        }
2925
2926        /**
2927         * Utility method for formatting using the x
2928         * conversion character.
2929         * @param sx the String to format, the result of
2930         *     converting a short, int, or long to a
2931         *     String.
2932         * @return the formatted String.
2933         */
2934        private String printXFormat(String sx) {
2935
2936            int nLeadingZeros = 0;
2937            int nBlanks = 0;
2938            if (sx.equals("0") && m_precisionSet && (m_precision == 0)) {
2939                sx = "";
2940            }
2941            if (m_precisionSet) {
2942                nLeadingZeros = m_precision - sx.length();
2943            }
2944            if (nLeadingZeros < 0) {
2945                nLeadingZeros = 0;
2946            }
2947            if (m_fieldWidthSet) {
2948                nBlanks = m_fieldWidth - nLeadingZeros - sx.length();
2949                if (m_alternateForm) {
2950                    nBlanks = nBlanks - 2;
2951                }
2952            }
2953            if (nBlanks < 0) {
2954                nBlanks = 0;
2955            }
2956            int n = 0;
2957            if (m_alternateForm) {
2958                n += 2;
2959            }
2960            n += nLeadingZeros;
2961            n += sx.length();
2962            n += nBlanks;
2963            char[] ca = new char[n];
2964            int i = 0;
2965            if (m_leftJustify) {
2966                if (m_alternateForm) {
2967                    ca[i++] = '0';
2968                    ca[i++] = 'x';
2969                }
2970                for (int j = 0; j < nLeadingZeros; j++, i++) {
2971                    ca[i] = '0';
2972                }
2973                char[] csx = sx.toCharArray();
2974                for (int j = 0; j < csx.length; j++, i++) {
2975                    ca[i] = csx[j];
2976                }
2977                for (int j = 0; j < nBlanks; j++, i++) {
2978                    ca[i] = ' ';
2979                }
2980            } else {
2981                if (!m_leadingZeros) {
2982                    for (int j = 0; j < nBlanks; j++, i++) {
2983                        ca[i] = ' ';
2984                    }
2985                }
2986                if (m_alternateForm) {
2987                    ca[i++] = '0';
2988                    ca[i++] = 'x';
2989                }
2990                if (m_leadingZeros) {
2991                    for (int j = 0; j < nBlanks; j++, i++) {
2992                        ca[i] = '0';
2993                    }
2994                }
2995                for (int j = 0; j < nLeadingZeros; j++, i++) {
2996                    ca[i] = '0';
2997                }
2998                char[] csx = sx.toCharArray();
2999                for (int j = 0; j < csx.length; j++, i++) {
3000                    ca[i] = csx[j];
3001                }
3002            }
3003            String caReturn = new String(ca);
3004            if (m_conversionCharacter == 'X') {
3005                caReturn = caReturn.toUpperCase();
3006            }
3007            return caReturn;
3008        }
3009
3010        /**
3011         * Store the digits <code>n</code> in %n$ forms.
3012         */
3013        private void setArgPosition() {
3014
3015            int xPos;
3016            for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
3017                if (!Character.isDigit(m_fmt.charAt(xPos))) {
3018                    break;
3019                }
3020            }
3021            if ((xPos > m_pos) && (xPos < m_fmt.length())) {
3022                if (m_fmt.charAt(xPos) == '$') {
3023                    m_positionalSpecification = true;
3024                    m_argumentPosition = Integer.parseInt(m_fmt.substring(m_pos, xPos));
3025                    m_pos = xPos + 1;
3026                }
3027            }
3028        }
3029
3030        /**
3031         * Check for a conversion character.  If it is
3032         * there, store it.
3033         * @return <code>true</code> if the conversion
3034         *     character is there, and
3035         *     <code>false</code> otherwise.
3036         */
3037        private boolean setConversionCharacter() {
3038
3039            /* idfgGoxXeEcs */
3040            boolean ret = false;
3041            m_conversionCharacter = '\0';
3042            if (m_pos < m_fmt.length()) {
3043                char c = m_fmt.charAt(m_pos);
3044                if ((c == 'i')
3045                    || (c == 'd')
3046                    || (c == 'f')
3047                    || (c == 'g')
3048                    || (c == 'G')
3049                    || (c == 'o')
3050                    || (c == 'x')
3051                    || (c == 'X')
3052                    || (c == 'e')
3053                    || (c == 'E')
3054                    || (c == 'c')
3055                    || (c == 's')
3056                    || (c == '%')) {
3057                    m_conversionCharacter = c;
3058                    m_pos++;
3059                    ret = true;
3060                }
3061            }
3062            return ret;
3063        }
3064
3065        /**
3066         * Set the field width.
3067         */
3068        private void setFieldWidth() {
3069
3070            int firstPos = m_pos;
3071            m_fieldWidth = 0;
3072            m_fieldWidthSet = false;
3073            if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '*')) {
3074                m_pos++;
3075                if (!setFieldWidthArgPosition()) {
3076                    m_variableFieldWidth = true;
3077                    m_fieldWidthSet = true;
3078                }
3079            } else {
3080                while (m_pos < m_fmt.length()) {
3081                    char c = m_fmt.charAt(m_pos);
3082                    if (Character.isDigit(c)) {
3083                        m_pos++;
3084                    } else {
3085                        break;
3086                    }
3087                }
3088                if ((firstPos < m_pos) && (firstPos < m_fmt.length())) {
3089                    String sz = m_fmt.substring(firstPos, m_pos);
3090                    m_fieldWidth = Integer.parseInt(sz);
3091                    m_fieldWidthSet = true;
3092                }
3093            }
3094        }
3095
3096        /**
3097         * Store the digits <code>n</code> in *n$ forms.
3098         *
3099         * @return the result
3100         */
3101        private boolean setFieldWidthArgPosition() {
3102
3103            boolean ret = false;
3104            int xPos;
3105            for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
3106                if (!Character.isDigit(m_fmt.charAt(xPos))) {
3107                    break;
3108                }
3109            }
3110            if ((xPos > m_pos) && (xPos < m_fmt.length())) {
3111                if (m_fmt.charAt(xPos) == '$') {
3112                    m_positionalFieldWidth = true;
3113                    m_argumentPositionForFieldWidth = Integer.parseInt(m_fmt.substring(m_pos, xPos));
3114                    m_pos = xPos + 1;
3115                    ret = true;
3116                }
3117            }
3118            return ret;
3119        }
3120
3121        /**
3122         * Set flag characters, one of '-+#0 or a space.
3123         */
3124        private void setFlagCharacters() {
3125
3126            /* '-+ #0 */
3127            m_thousands = false;
3128            m_leftJustify = false;
3129            m_leadingSign = false;
3130            m_leadingSpace = false;
3131            m_alternateForm = false;
3132            m_leadingZeros = false;
3133            for (; m_pos < m_fmt.length(); m_pos++) {
3134                char c = m_fmt.charAt(m_pos);
3135                if (c == '\'') {
3136                    m_thousands = true;
3137                } else if (c == '-') {
3138                    m_leftJustify = true;
3139                    m_leadingZeros = false;
3140                } else if (c == '+') {
3141                    m_leadingSign = true;
3142                    m_leadingSpace = false;
3143                } else if (c == ' ') {
3144                    if (!m_leadingSign) {
3145                        m_leadingSpace = true;
3146                    }
3147                } else if (c == '#') {
3148                    m_alternateForm = true;
3149                } else if (c == '0') {
3150                    if (!m_leftJustify) {
3151                        m_leadingZeros = true;
3152                    }
3153                } else {
3154                    break;
3155                }
3156            }
3157        }
3158
3159        /**
3160         * Check for an h, l, or L in a format.  An L is
3161         * used to control the minimum number of digits
3162         * in an exponent when using floating point
3163         * formats.  An l or h is used to control
3164         * conversion of the input to a long or short,
3165         * respectively, before formatting.  If any of
3166         * these is present, store them.
3167         */
3168        private void setOptionalHL() {
3169
3170            m_optionalh = false;
3171            m_optionall = false;
3172            m_optionalL = false;
3173            if (m_pos < m_fmt.length()) {
3174                char c = m_fmt.charAt(m_pos);
3175                if (c == 'h') {
3176                    m_optionalh = true;
3177                    m_pos++;
3178                } else if (c == 'l') {
3179                    m_optionall = true;
3180                    m_pos++;
3181                } else if (c == 'L') {
3182                    m_optionalL = true;
3183                    m_pos++;
3184                }
3185            }
3186        }
3187
3188        /**
3189         * Set the precision.
3190         */
3191        private void setPrecision() {
3192
3193            int firstPos = m_pos;
3194            m_precisionSet = false;
3195            if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '.')) {
3196                m_pos++;
3197                if ((m_pos < m_fmt.length()) && (m_fmt.charAt(m_pos) == '*')) {
3198                    m_pos++;
3199                    if (!setPrecisionArgPosition()) {
3200                        m_variablePrecision = true;
3201                        m_precisionSet = true;
3202                    }
3203                    return;
3204                } else {
3205                    while (m_pos < m_fmt.length()) {
3206                        char c = m_fmt.charAt(m_pos);
3207                        if (Character.isDigit(c)) {
3208                            m_pos++;
3209                        } else {
3210                            break;
3211                        }
3212                    }
3213                    if (m_pos > (firstPos + 1)) {
3214                        String sz = m_fmt.substring(firstPos + 1, m_pos);
3215                        m_precision = Integer.parseInt(sz);
3216                        m_precisionSet = true;
3217                    }
3218                }
3219            }
3220        }
3221
3222        /**
3223         * Store the digits <code>n</code> in *n$ forms.
3224         *
3225         * @return the result
3226         */
3227        private boolean setPrecisionArgPosition() {
3228
3229            boolean ret = false;
3230            int xPos;
3231            for (xPos = m_pos; xPos < m_fmt.length(); xPos++) {
3232                if (!Character.isDigit(m_fmt.charAt(xPos))) {
3233                    break;
3234                }
3235            }
3236            if ((xPos > m_pos) && (xPos < m_fmt.length())) {
3237                if (m_fmt.charAt(xPos) == '$') {
3238                    m_positionalPrecision = true;
3239                    m_argumentPositionForPrecision = Integer.parseInt(m_fmt.substring(m_pos, xPos));
3240                    m_pos = xPos + 1;
3241                    ret = true;
3242                }
3243            }
3244            return ret;
3245        }
3246
3247        /**
3248         * Start the symbolic carry process.  The process
3249         * is not quite finished because the symbolic
3250         * carry may change the length of the string and
3251         * change the exponent (in e format).
3252         * @param cLast index of the last digit changed
3253         *     by the round
3254         * @param cFirst index of the first digit allowed
3255         *     to be changed by this phase of the round
3256         * @param ca the ca parameter
3257         * @return <code>true</code> if the carry forces
3258         *     a round that will change the print still
3259         *     more
3260         */
3261        private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst) {
3262
3263            boolean carry = true;
3264            for (int i = cLast; carry && (i >= cFirst); i--) {
3265                carry = false;
3266                switch (ca[i]) {
3267                    case '0':
3268                        ca[i] = '1';
3269                        break;
3270                    case '1':
3271                        ca[i] = '2';
3272                        break;
3273                    case '2':
3274                        ca[i] = '3';
3275                        break;
3276                    case '3':
3277                        ca[i] = '4';
3278                        break;
3279                    case '4':
3280                        ca[i] = '5';
3281                        break;
3282                    case '5':
3283                        ca[i] = '6';
3284                        break;
3285                    case '6':
3286                        ca[i] = '7';
3287                        break;
3288                    case '7':
3289                        ca[i] = '8';
3290                        break;
3291                    case '8':
3292                        ca[i] = '9';
3293                        break;
3294                    case '9':
3295                        ca[i] = '0';
3296                        carry = true;
3297                        break;
3298                    default:
3299                        // noop
3300                }
3301            }
3302            return carry;
3303        }
3304    }
3305
3306    /** Character position. Used by the constructor. */
3307    DecimalFormatSymbols m_dfs;
3308
3309    /** Character position. Used by the constructor. */
3310    private int m_cPos;
3311
3312    /** Vector of control strings and format literals. */
3313    private Vector<ConversionSpecification> m_vFmt = new Vector<ConversionSpecification>();
3314
3315    /**
3316     * Constructs an array of control specifications
3317     * possibly preceded, separated, or followed by
3318     * ordinary strings.  Control strings begin with
3319     * unpaired percent signs.  A pair of successive
3320     * percent signs designates a single percent sign in
3321     * the format.
3322     * @param locale the locale
3323     * @param fmtArg  Control string.
3324     * @exception CmsIllegalArgumentException if the control
3325     * string is null, zero length, or otherwise
3326     * malformed.
3327     */
3328    public PrintfFormat(Locale locale, String fmtArg)
3329    throws CmsIllegalArgumentException {
3330
3331        m_dfs = new DecimalFormatSymbols(locale);
3332        int ePos = 0;
3333        ConversionSpecification sFmt = null;
3334        String unCS = nonControl(fmtArg, 0);
3335        if (unCS != null) {
3336            sFmt = new ConversionSpecification();
3337            sFmt.setLiteral(unCS);
3338            m_vFmt.addElement(sFmt);
3339        }
3340        while ((m_cPos != -1) && (m_cPos < fmtArg.length())) {
3341            for (ePos = m_cPos + 1; ePos < fmtArg.length(); ePos++) {
3342                char c = 0;
3343                c = fmtArg.charAt(ePos);
3344                if (c == 'i') {
3345                    break;
3346                }
3347                if (c == 'd') {
3348                    break;
3349                }
3350                if (c == 'f') {
3351                    break;
3352                }
3353                if (c == 'g') {
3354                    break;
3355                }
3356                if (c == 'G') {
3357                    break;
3358                }
3359                if (c == 'o') {
3360                    break;
3361                }
3362                if (c == 'x') {
3363                    break;
3364                }
3365                if (c == 'X') {
3366                    break;
3367                }
3368                if (c == 'e') {
3369                    break;
3370                }
3371                if (c == 'E') {
3372                    break;
3373                }
3374                if (c == 'c') {
3375                    break;
3376                }
3377                if (c == 's') {
3378                    break;
3379                }
3380                if (c == '%') {
3381                    break;
3382                }
3383            }
3384            ePos = Math.min(ePos + 1, fmtArg.length());
3385            sFmt = new ConversionSpecification(fmtArg.substring(m_cPos, ePos));
3386            m_vFmt.addElement(sFmt);
3387            unCS = nonControl(fmtArg, ePos);
3388            if (unCS != null) {
3389                sFmt = new ConversionSpecification();
3390                sFmt.setLiteral(unCS);
3391                m_vFmt.addElement(sFmt);
3392            }
3393        }
3394    }
3395
3396    /**
3397     * Constructs an array of control specifications
3398     * possibly preceded, separated, or followed by
3399     * ordinary strings.  Control strings begin with
3400     * unpaired percent signs.  A pair of successive
3401     * percent signs designates a single percent sign in
3402     * the format.
3403     * @param fmtArg  Control string.
3404     * @exception IllegalArgumentException if the control
3405     * string is null, zero length, or otherwise
3406     * malformed.
3407     */
3408    public PrintfFormat(String fmtArg)
3409    throws IllegalArgumentException {
3410
3411        this(Locale.getDefault(), fmtArg);
3412    }
3413
3414    /**
3415     * Format nothing.  Just use the control string.
3416     * @return  the formatted String.
3417     */
3418    public String sprintf() {
3419
3420        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3421        ConversionSpecification cs = null;
3422        char c = 0;
3423        StringBuffer sb = new StringBuffer();
3424        while (e.hasMoreElements()) {
3425            cs = e.nextElement();
3426            c = cs.getConversionCharacter();
3427            if (c == '\0') {
3428                sb.append(cs.getLiteral());
3429            } else if (c == '%') {
3430                sb.append("%");
3431            }
3432        }
3433        return sb.toString();
3434    }
3435
3436    /**
3437     * Format a double.
3438     * @param x The double to format.
3439     * @return  The formatted String.
3440     * @exception CmsIllegalArgumentException if the
3441     *     conversion character is c, C, s, S,
3442     *     d, d, x, X, or o.
3443     */
3444    public String sprintf(double x) throws CmsIllegalArgumentException {
3445
3446        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3447        ConversionSpecification cs = null;
3448        char c = 0;
3449        StringBuffer sb = new StringBuffer();
3450        while (e.hasMoreElements()) {
3451            cs = e.nextElement();
3452            c = cs.getConversionCharacter();
3453            if (c == '\0') {
3454                sb.append(cs.getLiteral());
3455            } else if (c == '%') {
3456                sb.append("%");
3457            } else {
3458                sb.append(cs.internalsprintf(x));
3459            }
3460        }
3461        return sb.toString();
3462    }
3463
3464    /**
3465     * Format an int.
3466     * @param x The int to format.
3467     * @return  The formatted String.
3468     * @exception CmsIllegalArgumentException if the
3469     *     conversion character is f, e, E, g, G, s,
3470     *     or S.
3471     */
3472    public String sprintf(int x) throws CmsIllegalArgumentException {
3473
3474        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3475        ConversionSpecification cs = null;
3476        char c = 0;
3477        StringBuffer sb = new StringBuffer();
3478        while (e.hasMoreElements()) {
3479            cs = e.nextElement();
3480            c = cs.getConversionCharacter();
3481            if (c == '\0') {
3482                sb.append(cs.getLiteral());
3483            } else if (c == '%') {
3484                sb.append("%");
3485            } else {
3486                sb.append(cs.internalsprintf(x));
3487            }
3488        }
3489        return sb.toString();
3490    }
3491
3492    /**
3493     * Format an long.
3494     * @param x The long to format.
3495     * @return  The formatted String.
3496     * @exception CmsIllegalArgumentException if the
3497     *     conversion character is f, e, E, g, G, s,
3498     *     or S.
3499     */
3500    public String sprintf(long x) throws CmsIllegalArgumentException {
3501
3502        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3503        ConversionSpecification cs = null;
3504        char c = 0;
3505        StringBuffer sb = new StringBuffer();
3506        while (e.hasMoreElements()) {
3507            cs = e.nextElement();
3508            c = cs.getConversionCharacter();
3509            if (c == '\0') {
3510                sb.append(cs.getLiteral());
3511            } else if (c == '%') {
3512                sb.append("%");
3513            } else {
3514                sb.append(cs.internalsprintf(x));
3515            }
3516        }
3517        return sb.toString();
3518    }
3519
3520    /**
3521     * Format an Object.  Convert wrapper types to
3522     * their primitive equivalents and call the
3523     * appropriate internal formatting method. Convert
3524     * Strings using an internal formatting method for
3525     * Strings. Otherwise use the default formatter
3526     * (use toString).
3527     * @param x the Object to format.
3528     * @return  the formatted String.
3529     * @exception CmsIllegalArgumentException if the
3530     *    conversion character is inappropriate for
3531     *    formatting an unwrapped value.
3532     */
3533    public String sprintf(Object x) throws CmsIllegalArgumentException {
3534
3535        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3536        ConversionSpecification cs = null;
3537        char c = 0;
3538        StringBuffer sb = new StringBuffer();
3539        while (e.hasMoreElements()) {
3540            cs = e.nextElement();
3541            c = cs.getConversionCharacter();
3542            if (c == '\0') {
3543                sb.append(cs.getLiteral());
3544            } else if (c == '%') {
3545                sb.append("%");
3546            } else {
3547                if (x instanceof Byte) {
3548                    sb.append(cs.internalsprintf(((Byte)x).byteValue()));
3549                } else if (x instanceof Short) {
3550                    sb.append(cs.internalsprintf(((Short)x).shortValue()));
3551                } else if (x instanceof Integer) {
3552                    sb.append(cs.internalsprintf(((Integer)x).intValue()));
3553                } else if (x instanceof Long) {
3554                    sb.append(cs.internalsprintf(((Long)x).longValue()));
3555                } else if (x instanceof Float) {
3556                    sb.append(cs.internalsprintf(((Float)x).floatValue()));
3557                } else if (x instanceof Double) {
3558                    sb.append(cs.internalsprintf(((Double)x).doubleValue()));
3559                } else if (x instanceof Character) {
3560                    sb.append(cs.internalsprintf(((Character)x).charValue()));
3561                } else if (x instanceof String) {
3562                    sb.append(cs.internalsprintf((String)x));
3563                } else {
3564                    sb.append(cs.internalsprintf(x));
3565                }
3566            }
3567        }
3568        return sb.toString();
3569    }
3570
3571    /**
3572     * Format an array of objects.  Byte, Short,
3573     * Integer, Long, Float, Double, and Character
3574     * arguments are treated as wrappers for primitive
3575     * types.
3576     * @param o The array of objects to format.
3577     * @return  The formatted String.
3578     */
3579    public String sprintf(Object[] o) {
3580
3581        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3582        ConversionSpecification cs = null;
3583        char c = 0;
3584        int i = 0;
3585        StringBuffer sb = new StringBuffer();
3586        while (e.hasMoreElements()) {
3587            cs = e.nextElement();
3588            c = cs.getConversionCharacter();
3589            if (c == '\0') {
3590                sb.append(cs.getLiteral());
3591            } else if (c == '%') {
3592                sb.append("%");
3593            } else {
3594                if (cs.isPositionalSpecification()) {
3595                    i = cs.getArgumentPosition() - 1;
3596                    if (cs.isPositionalFieldWidth()) {
3597                        int ifw = cs.getArgumentPositionForFieldWidth() - 1;
3598                        cs.setFieldWidthWithArg(((Integer)o[ifw]).intValue());
3599                    }
3600                    if (cs.isPositionalPrecision()) {
3601                        int ipr = cs.getArgumentPositionForPrecision() - 1;
3602                        cs.setPrecisionWithArg(((Integer)o[ipr]).intValue());
3603                    }
3604                } else {
3605                    if (cs.isVariableFieldWidth()) {
3606                        cs.setFieldWidthWithArg(((Integer)o[i]).intValue());
3607                        i++;
3608                    }
3609                    if (cs.isVariablePrecision()) {
3610                        cs.setPrecisionWithArg(((Integer)o[i]).intValue());
3611                        i++;
3612                    }
3613                }
3614                if (o[i] instanceof Byte) {
3615                    sb.append(cs.internalsprintf(((Byte)o[i]).byteValue()));
3616                } else if (o[i] instanceof Short) {
3617                    sb.append(cs.internalsprintf(((Short)o[i]).shortValue()));
3618                } else if (o[i] instanceof Integer) {
3619                    sb.append(cs.internalsprintf(((Integer)o[i]).intValue()));
3620                } else if (o[i] instanceof Long) {
3621                    sb.append(cs.internalsprintf(((Long)o[i]).longValue()));
3622                } else if (o[i] instanceof Float) {
3623                    sb.append(cs.internalsprintf(((Float)o[i]).floatValue()));
3624                } else if (o[i] instanceof Double) {
3625                    sb.append(cs.internalsprintf(((Double)o[i]).doubleValue()));
3626                } else if (o[i] instanceof Character) {
3627                    sb.append(cs.internalsprintf(((Character)o[i]).charValue()));
3628                } else if (o[i] instanceof String) {
3629                    sb.append(cs.internalsprintf((String)o[i]));
3630                } else {
3631                    sb.append(cs.internalsprintf(o[i]));
3632                }
3633                if (!cs.isPositionalSpecification()) {
3634                    i++;
3635                }
3636            }
3637        }
3638        return sb.toString();
3639    }
3640
3641    /**
3642     * Format a String.
3643     * @param x The String to format.
3644     * @return  The formatted String.
3645     * @exception CmsIllegalArgumentException if the
3646     *   conversion character is neither s nor S.
3647     */
3648    public String sprintf(String x) throws CmsIllegalArgumentException {
3649
3650        Enumeration<ConversionSpecification> e = m_vFmt.elements();
3651        ConversionSpecification cs = null;
3652        char c = 0;
3653        StringBuffer sb = new StringBuffer();
3654        while (e.hasMoreElements()) {
3655            cs = e.nextElement();
3656            c = cs.getConversionCharacter();
3657            if (c == '\0') {
3658                sb.append(cs.getLiteral());
3659            } else if (c == '%') {
3660                sb.append("%");
3661            } else {
3662                sb.append(cs.internalsprintf(x));
3663            }
3664        }
3665        return sb.toString();
3666    }
3667
3668    /**
3669     * Return a substring starting at
3670     * <code>start</code> and ending at either the end
3671     * of the String <code>s</code>, the next unpaired
3672     * percent sign, or at the end of the String if the
3673     * last character is a percent sign.
3674     * @param s  Control string.
3675     * @param start Position in the string
3676     *     <code>s</code> to begin looking for the start
3677     *     of a control string.
3678     * @return the substring from the start position
3679     *     to the beginning of the control string.
3680     */
3681    private String nonControl(String s, int start) {
3682
3683        m_cPos = s.indexOf("%", start);
3684        if (m_cPos == -1) {
3685            m_cPos = s.length();
3686        }
3687        return s.substring(start, m_cPos);
3688    }
3689}