KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > util > Format


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/util/Format.java#24 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2000-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2006 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 2 November, 2000
12 */

13
14 package mondrian.util;
15 import mondrian.olap.Util;
16
17 import java.io.PrintWriter JavaDoc;
18 import java.math.BigDecimal JavaDoc;
19 import java.math.BigInteger JavaDoc;
20 import java.text.*;
21 import java.util.*;
22
23 /**
24  * <code>Format</code> formats numbers, strings and dates according to the
25  * same specification as Visual Basic's
26  * <code>format()</code> function. This function is described in more detail
27  * <a HREF="http://www.apostate.com/programming/vb-format.html">here</a>. We
28  * have made the following enhancements to this specification:<ul>
29  *
30  * <li>if the international currency symbol (&#x00a4;) occurs in a format
31  * string, it is translated to the locale's currency symbol.</li>
32  *
33  * <li>the format string "Currency" is translated to the locale's currency
34  * format string. Negative currency values appear in parentheses.</li>
35  *
36  * <li>the string "USD" (abbreviation for U.S. Dollars) may occur in a format
37  * string.</li>
38  *
39  * </ul>
40  *
41  * <p>One format object can be used to format multiple values, thereby
42  * amortizing the time required to parse the format string. Example:</p>
43  *
44  * <pre><code>
45  * double[] values;
46  * Format format = new Format("##,##0.###;(##,##0.###);;Nil");
47  * for (int i = 0; i < values.length; i++) {
48  * System.out.println("Value #" + i + " is " + format.format(values[i]));
49  * }
50  * </code></pre>
51  *
52  * <p>Still to be implemented:<ul>
53  *
54  * <li>String formatting (upper-case, lower-case, fill from left/right)</li>
55  *
56  * <li>Use client's timezone for printing times.</li>
57  *
58  * </ul>
59  *
60  * @author jhyde
61  * @version $Id: //open/mondrian/src/main/mondrian/util/Format.java#24 $
62  */

63 public class Format {
64     private String JavaDoc formatString;
65     private BasicFormat format;
66     private FormatLocale locale;
67     private static final FieldPosition dummyFieldPos = createDummyFieldPos();
68
69     /**
70      * Maximum number of entries in the format cache used by
71      * {@link #get(String, java.util.Locale)}.
72      */

73     public static final int CacheLimit = 1000;
74
75     /**
76      * Gets the dummy implementation of {@link FieldPosition} which the JDK
77      * uses when you don't care about the status of a call to
78      * {@link Format#format}.
79      */

80     private static FieldPosition createDummyFieldPos() {
81         final FieldPosition[] pos = {null};
82         new DecimalFormat() {
83             public StringBuffer JavaDoc format(
84                     double number,
85                     StringBuffer JavaDoc result,
86                     FieldPosition fieldPosition) {
87                 pos[0] = fieldPosition;
88                 return result;
89             }
90         }.format(0.0);
91         return pos[0];
92     }
93
94     /**
95      * Maps (formatString, locale) pairs to {@link Format} objects.
96      *
97      * <p>If the number of entries in the cache exceeds 1000,
98      */

99     private static Map<String JavaDoc, Format> cache =
100         new LinkedHashMap<String JavaDoc, Format>() {
101             public boolean removeEldestEntry(Map.Entry<String JavaDoc, Format> entry) {
102                 return size() > CacheLimit;
103             }
104         };
105
106     static final char[] digits = {
107         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
108     };
109
110     static final char thousandSeparator_en = ',';
111     static final char decimalPlaceholder_en = '.';
112     static final String JavaDoc dateSeparator_en = "/";
113     static final String JavaDoc timeSeparator_en = ":";
114     static final String JavaDoc currencySymbol_en = "$";
115     static final String JavaDoc currencyFormat_en = "$#,##0.00";
116     static final String JavaDoc[] daysOfWeekShort_en = {
117         "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
118     };
119     static final String JavaDoc[] daysOfWeekLong_en = {
120         "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
121         "Saturday"
122     };
123     static final String JavaDoc[] monthsShort_en = {
124         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
125         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "",
126     };
127     static final String JavaDoc[] monthsLong_en = {
128         "January", "February", "March", "April", "May", "June",
129         "July", "August", "September", "October", "November", "December", "",
130     };
131     static final char intlCurrencySymbol = '\u08a4';
132
133     /**
134      * Maps strings representing locales (for example, "en_US_Boston", "en_US",
135      * "en", or "" for the default) to a {@link Format.FormatLocale}.
136      */

137     private static final Map<String JavaDoc, FormatLocale> mapLocaleToFormatLocale =
138         new HashMap<String JavaDoc, FormatLocale>();
139
140     /**
141      * Locale for US English, also the default for English and for all
142      * locales.
143      */

144     static final FormatLocale locale_US = createLocale(
145         '\0', '\0', null, null, null, null, null, null, null, null,
146         Locale.US);
147
148     /**
149      * Formats an object using a format string, according to a given locale.
150      *
151      * <p>If you need to format many objects using the same format string,
152      * create a formatter object using
153      * {@link mondrian.util.Format#Format(String, java.util.Locale)}.
154      */

155     static String JavaDoc format(Object JavaDoc o, String JavaDoc formatString, Locale locale)
156     {
157         Format format = new Format(formatString, locale);
158         return format.format(o);
159     }
160
161     static class Token {
162         int code;
163         int flags;
164         String JavaDoc token;
165
166         Token(int code, int flags, String JavaDoc token)
167         {
168             this.code = code;
169             this.flags = flags;
170             this.token = token;
171         }
172
173         boolean isSpecial()
174         {
175             return (flags & SPECIAL) == SPECIAL;
176         }
177
178         boolean isNumeric()
179         {
180             return (flags & NUMERIC) == NUMERIC;
181         }
182
183         boolean isDate()
184         {
185             return (flags & DATE) == DATE;
186         }
187
188         boolean isString()
189         {
190             return (flags & STRING) == STRING;
191         }
192
193         BasicFormat makeFormat(FormatLocale locale)
194         {
195             if (isDate()) {
196                 return new DateFormat(code, token, locale, false);
197             } else if (isNumeric()) {
198                 return new LiteralFormat(code, token);
199             } else if (isString()) {
200                 throw new Error JavaDoc();
201             } else {
202                 return new LiteralFormat(token);
203             }
204         }
205     }
206
207     /**
208      * BasicFormat is the interface implemented by the classes which do all
209      * the work. Whereas {@link Format} has only one method for formatting,
210      * {@link Format#format(Object)}, this class provides methods for several
211      * primitive types. To make it easy to combine formatting objects, all
212      * methods write to a {@link PrintWriter}.
213      *
214      * The base implementation of most of these methods throws an error, there
215      * is no requirement that a derived class implements all of these methods.
216      * It is up to {@link Format#parseFormatString} to ensure that, for example,
217      * the {@link #format(double,StringBuilder)} method is never called for
218      * {@link DateFormat}.
219      */

220     static class BasicFormat {
221         int code;
222
223         BasicFormat() {
224             this(0);
225         }
226
227         BasicFormat(int code) {
228             this.code = code;
229         }
230
231         boolean isNumeric() {
232             return false;
233         }
234
235         boolean isDate() {
236             return false;
237         }
238
239         boolean isString() {
240             return false;
241         }
242
243         void formatNull(StringBuilder JavaDoc buf) {
244         }
245
246         void format(double d, StringBuilder JavaDoc buf) {
247             throw new Error JavaDoc();
248         }
249
250         void format(long n, StringBuilder JavaDoc buf) {
251             throw new Error JavaDoc();
252         }
253
254         void format(String JavaDoc s, StringBuilder JavaDoc buf) {
255             throw new Error JavaDoc();
256         }
257
258         void format(Date date, StringBuilder JavaDoc buf) {
259             Calendar calendar = Calendar.getInstance(); // todo: use locale
260
calendar.setTime(date);
261             format(calendar, buf);
262         }
263
264         void format(Calendar calendar, StringBuilder JavaDoc buf) {
265             throw new Error JavaDoc();
266         }
267
268         /**
269          * Returns whether this format can handle a given value.
270          *
271          * <p>Usually returns true;
272          * one notable exception is a format for negative numbers which
273          * causes the number to be underflow to zero and therefore be
274          * ineligible for the negative format.
275          *
276          * @param n value
277          * @return Whether this format is applicable for a given value
278          */

279         boolean isApplicableTo(double n) {
280             return true;
281         }
282
283         /**
284          * Returns whether this format can handle a given value.
285          *
286          * <p>Usually returns true;
287          * one notable exception is a format for negative numbers which
288          * causes the number to be underflow to zero and therefore be
289          * ineligible for the negative format.
290          *
291          * @param n value
292          * @return Whether this format is applicable for a given value
293          */

294         boolean isApplicableTo(long n) {
295             return true;
296         }
297     }
298
299     /**
300      * AlternateFormat is an implementation of {@link Format.BasicFormat} which
301      * allows a different format to be used for different kinds of values. If
302      * there are 4 formats, purposes are as follows:<ol>
303      * <li>positive numbers</li>
304      * <li>negative numbers</li>
305      * <li>zero</li>
306      * <li>null values</li>
307      * </ol>
308      *
309      * If there are fewer than 4 formats, the first is used as a fall-back.
310      * See the <a HREF="http://www.apostate.com/programming/vb-format.html">the
311      * visual basic format specification</a> for more details.
312      */

313     static class AlternateFormat extends BasicFormat {
314         final BasicFormat[] formats;
315
316         AlternateFormat(BasicFormat[] formats)
317         {
318             this.formats = formats;
319             assert formats.length >= 2;
320         }
321
322         void formatNull(StringBuilder JavaDoc buf) {
323             if (formats.length >= 4) {
324                 formats[3].format(0, buf);
325             } else {
326                 super.formatNull(buf);
327             }
328         }
329
330         void format(double n, StringBuilder JavaDoc buf) {
331             if (formats.length == 0) {
332                 buf.append(n);
333             } else {
334                 int i;
335                 if (n == 0 &&
336                     formats.length >= 3 &&
337                     formats[2] != null) {
338                     i = 2;
339                 } else if (n < 0 &&
340                            formats.length >= 2 &&
341                            formats[1] != null) {
342                     if (formats[1].isApplicableTo(n)) {
343                         n = -n;
344                         i = 1;
345                     } else {
346                         // Does not fit into the negative mask, so use the nil
347
// mask. For example, "#.0;(#.0);Nil" formats -0.0001
348
// as "Nil".
349
i = 2;
350                     }
351                 } else {
352                     i = 0;
353                 }
354                 formats[i].format(n, buf);
355             }
356         }
357
358         void format(long n, StringBuilder JavaDoc buf) {
359             if (formats.length == 0) {
360                 buf.append(n);
361             } else {
362                 int i;
363                 if (n == 0 &&
364                     formats.length >= 3 &&
365                     formats[2] != null) {
366                     i = 2;
367                 } else if (n < 0 &&
368                            formats.length >= 2 &&
369                            formats[1] != null &&
370                            formats[1].isApplicableTo(n)) {
371                     n = -n;
372                     i = 1;
373                 } else {
374                     i = 0;
375                 }
376                 formats[i].format(n, buf);
377             }
378         }
379
380         void format(String JavaDoc s, StringBuilder JavaDoc buf) {
381             formats[0].format(s, buf);
382         }
383
384         void format(Date date, StringBuilder JavaDoc buf) {
385             formats[0].format(date, buf);
386         }
387
388         void format(Calendar calendar, StringBuilder JavaDoc buf) {
389             formats[0].format(calendar, buf);
390         }
391     }
392
393     /**
394      * LiteralFormat is an implementation of {@link Format.BasicFormat} which
395      * prints a constant value, regardless of the value to be formatted.
396      *
397      * @see CompoundFormat
398      */

399     static class LiteralFormat extends BasicFormat
400     {
401         String JavaDoc s;
402
403         LiteralFormat(String JavaDoc s)
404         {
405             this(FORMAT_LITERAL, s);
406         }
407
408         LiteralFormat(int code, String JavaDoc s)
409         {
410             super(code);
411             this.s = s;
412         }
413
414         void format(double d, StringBuilder JavaDoc buf) {
415             buf.append(s);
416         }
417
418         void format(long n, StringBuilder JavaDoc buf) {
419             buf.append(s);
420         }
421
422         void format(String JavaDoc s, StringBuilder JavaDoc buf) {
423             buf.append(s);
424         }
425
426         void format(Date date, StringBuilder JavaDoc buf) {
427             buf.append(s);
428         }
429
430         void format(Calendar calendar, StringBuilder JavaDoc buf) {
431             buf.append(s);
432         }
433     }
434
435     /**
436      * CompoundFormat is an implementation of {@link Format.BasicFormat} where
437      * each value is formatted by applying a sequence of format elements. Each
438      * format element is itself a format.
439      *
440      * @see AlternateFormat
441      */

442     static class CompoundFormat extends BasicFormat
443     {
444         final BasicFormat[] formats;
445         CompoundFormat(BasicFormat[] formats)
446         {
447             this.formats = formats;
448             assert formats.length >= 2;
449         }
450
451         void formatNull(StringBuilder JavaDoc buf) {
452             for (int i = 0; i < formats.length; i++) {
453                 formats[i].formatNull(buf);
454             }
455         }
456
457         void format(double v, StringBuilder JavaDoc buf) {
458             for (int i = 0; i < formats.length; i++) {
459                 formats[i].format(v, buf);
460             }
461         }
462
463         void format(long v, StringBuilder JavaDoc buf) {
464             for (int i = 0; i < formats.length; i++) {
465                 formats[i].format(v, buf);
466             }
467         }
468
469         void format(String JavaDoc v, StringBuilder JavaDoc buf) {
470             for (int i = 0; i < formats.length; i++) {
471                 formats[i].format(v, buf);
472             }
473         }
474
475         void format(Date v, StringBuilder JavaDoc buf) {
476             for (int i = 0; i < formats.length; i++) {
477                 formats[i].format(v, buf);
478             }
479         }
480
481         void format(Calendar v, StringBuilder JavaDoc buf) {
482             for (int i = 0; i < formats.length; i++) {
483                 formats[i].format(v, buf);
484             }
485         }
486
487         boolean isApplicableTo(double n) {
488             for (int i = 0; i < formats.length; i++) {
489                 if (!formats[i].isApplicableTo(n)) {
490                     return false;
491                 }
492             }
493             return true;
494         }
495
496     }
497
498     /**
499      * JavaFormat is an implementation of {@link Format.BasicFormat} which
500      * prints values using Java's default formatting for their type.
501      * <code>null</code> values appear as an empty string.
502      */

503     static class JavaFormat extends BasicFormat
504     {
505         private final NumberFormat numberFormat;
506         private final java.text.DateFormat JavaDoc dateFormat;
507
508         JavaFormat(Locale locale)
509         {
510             this.numberFormat = NumberFormat.getNumberInstance(locale);
511             this.dateFormat = java.text.DateFormat.getDateInstance(
512                     java.text.DateFormat.SHORT, locale);
513         }
514
515         // No need to override format(Object,PrintWriter) or
516
// format(Date,PrintWriter).
517

518         void format(double d, StringBuilder JavaDoc buf) {
519             // NOTE (jhyde, 2006/12/1): We'd use
520
// NumberFormat(double,StringBuilder,FieldPosition) if it existed.
521
buf.append(numberFormat.format(d));
522         }
523
524         void format(long n, StringBuilder JavaDoc buf) {
525             // NOTE (jhyde, 2006/12/1): We'd use
526
// NumberFormat(long,StringBuilder,FieldPosition) if it existed.
527
buf.append(numberFormat.format(n));
528         }
529
530         void format(String JavaDoc s, StringBuilder JavaDoc buf) {
531             buf.append(s);
532         }
533
534         void format(Calendar calendar, StringBuilder JavaDoc buf) {
535             // NOTE (jhyde, 2006/12/1): We'd use
536
// NumberFormat(Date,StringBuilder,FieldPosition) if it existed.
537
buf.append(dateFormat.format(calendar.getTime()));
538         }
539     }
540
541     /**
542      * FallbackFormat catches un-handled datatypes and prints the original
543      * format string. Better than giving an error. Abstract base class for
544      * NumericFormat and DateFormat.
545      */

546     static abstract class FallbackFormat extends BasicFormat
547     {
548         String JavaDoc token;
549
550         FallbackFormat(int code, String JavaDoc token)
551         {
552             super(code);
553             this.token = token;
554         }
555
556         void format(double d, StringBuilder JavaDoc buf) {
557             buf.append(token);
558         }
559
560         void format(long n, StringBuilder JavaDoc buf) {
561             buf.append(token);
562         }
563
564         void format(String JavaDoc s, StringBuilder JavaDoc buf) {
565             buf.append(token);
566         }
567
568         void format(Calendar calendar, StringBuilder JavaDoc buf) {
569             buf.append(token);
570         }
571     }
572
573     /**
574      * NumericFormat is an implementation of {@link Format.BasicFormat} which
575      * prints numbers with a given number of decimal places, leading zeroes, in
576      * exponential notation, etc.
577      *
578      * <p>It is implemented using {@link FloatingDecimal}, which is a
579      * barely-modified version of <code>java.lang.FloatingDecimal</code>.
580      */

581     static class NumericFormat extends FallbackFormat
582     {
583         FormatLocale locale;
584         int digitsLeftOfPoint;
585         int zeroesLeftOfPoint;
586         int digitsRightOfPoint;
587         int zeroesRightOfPoint;
588         int digitsRightOfExp;
589         int zeroesRightOfExp;
590
591         /**
592          * Number of decimal places to shift the number left before
593          * formatting it: 2 means multiply by 100; -3 means divide by
594          * 1000.
595          */

596         int decimalShift;
597         char expChar;
598         boolean expSign;
599         boolean useDecimal; // not used
600
boolean useThouSep;
601
602         NumericFormat(
603             String JavaDoc token, FormatLocale locale,
604             int expFormat,
605             int digitsLeftOfPoint, int zeroesLeftOfPoint,
606             int digitsRightOfPoint, int zeroesRightOfPoint,
607             int digitsRightOfExp, int zeroesRightOfExp,
608             boolean useDecimal, boolean useThouSep)
609         {
610             super(FORMAT_NULL, token);
611             this.locale = locale;
612             switch (expFormat) {
613             case FORMAT_E_MINUS_UPPER:
614                 this.expChar = 'E';
615                 this.expSign = false;
616                 break;
617             case FORMAT_E_PLUS_UPPER:
618                 this.expChar = 'E';
619                 this.expSign = true;
620                 break;
621             case FORMAT_E_MINUS_LOWER:
622                 this.expChar = 'e';
623                 this.expSign = false;
624                 break;
625             case FORMAT_E_PLUS_LOWER:
626                 this.expChar = 'e';
627                 this.expSign = true;
628                 break;
629             default:
630                 this.expChar = 0;
631                 this.expSign = false;
632             }
633             this.digitsLeftOfPoint = digitsLeftOfPoint;
634             this.zeroesLeftOfPoint = zeroesLeftOfPoint;
635             this.digitsRightOfPoint = digitsRightOfPoint;
636             this.zeroesRightOfPoint = zeroesRightOfPoint;
637             this.digitsRightOfExp = digitsRightOfExp;
638             this.zeroesRightOfExp = zeroesRightOfExp;
639             this.useDecimal = useDecimal;
640             this.useThouSep = useThouSep;
641             this.decimalShift = 0; // set later
642
}
643
644         void format(double n, StringBuilder JavaDoc buf)
645         {
646             FloatingDecimal fd = new FloatingDecimal(n);
647             fd.shift(decimalShift);
648             final int formatDigitsRightOfPoint =
649                     zeroesRightOfPoint + digitsRightOfPoint;
650             if (n < 0 && !shows(fd, formatDigitsRightOfPoint)) {
651                 // Underflow of negative number. Make it zero, so there is no
652
// '-' sign.
653
n = 0;
654                 fd = new FloatingDecimal(0);
655             }
656             String JavaDoc s = fd.toJavaFormatString(
657                 zeroesLeftOfPoint,
658                 locale.decimalPlaceholder,
659                 zeroesRightOfPoint,
660                     formatDigitsRightOfPoint,
661                 expChar,
662                 expSign,
663                 zeroesRightOfExp,
664                 useThouSep ? locale.thousandSeparator : '\0');
665             buf.append(s);
666         }
667
668         boolean isApplicableTo(double n) {
669             if (n >= 0) {
670                 return true;
671             }
672             FloatingDecimal fd = new FloatingDecimal(n);
673             fd.shift(decimalShift);
674             final int formatDigitsRightOfPoint =
675                     zeroesRightOfPoint + digitsRightOfPoint;
676             return shows(fd, formatDigitsRightOfPoint);
677         }
678
679         private static boolean shows(
680                 FloatingDecimal fd, int formatDigitsRightOfPoint) {
681             final int i0 = - fd.decExponent - formatDigitsRightOfPoint;
682             if (i0 < 0) {
683                 return true;
684             }
685             if (i0 > 0) {
686                 return false;
687             }
688             for (int i = i0; i < fd.nDigits; ++i) {
689                 final char digit = fd.digits[i];
690                 if (i == i0) {
691                     if (digit > '5') {
692                         return true;
693                     }
694                     if (digit < '5') {
695                         return false;
696                     }
697                 } else {
698                     if (digit > '0') {
699                         return true;
700                     }
701                 }
702             }
703             return false;
704         }
705
706         void format(long n, StringBuilder JavaDoc buf)
707         {
708             mondrian.util.Format.FloatingDecimal fd
709                 = new mondrian.util.Format.FloatingDecimal(n);
710             fd.shift(decimalShift);
711             String JavaDoc s = fd.toJavaFormatString(
712                 zeroesLeftOfPoint,
713                 locale.decimalPlaceholder,
714                 zeroesRightOfPoint,
715                 zeroesRightOfPoint + digitsRightOfPoint,
716                 expChar,
717                 expSign,
718                 zeroesRightOfExp,
719                 useThouSep ? locale.thousandSeparator : '\0');
720             buf.append(s);
721         }
722     }
723
724     /**
725      * DateFormat is an element of a {@link Format.CompoundFormat} which has a
726      * value when applied to a {@link Calendar} object. (Values of type {@link
727      * Date} are automatically converted into {@link Calendar}s when you call
728      * {@link Format.BasicFormat#format(java.util.Date,StringBuilder)} calls to format
729      * other kinds of values give a runtime error.)
730      *
731      * <p>In a typical use of this class, a format string such as "m/d/yy" is
732      * parsed into DateFormat objects for "m", "d", and "yy", and {@link
733      * LiteralFormat} objects for "/". A {@link Format.CompoundFormat} object
734      * is created to bind them together.
735      */

736     static class DateFormat extends FallbackFormat
737     {
738         FormatLocale locale;
739         boolean twelveHourClock;
740
741         DateFormat(int code, String JavaDoc s, FormatLocale locale, boolean twelveHourClock)
742         {
743             super(code, s);
744             this.locale = locale;
745             this.twelveHourClock = twelveHourClock;
746         }
747
748         void setTwelveHourClock(boolean twelveHourClock)
749         {
750             this.twelveHourClock = twelveHourClock;
751         }
752
753         void format(Calendar calendar, StringBuilder JavaDoc buf)
754         {
755             format(code, calendar, buf);
756         }
757
758         private void format(int code, Calendar calendar, StringBuilder JavaDoc buf)
759         {
760             switch (code) {
761             case FORMAT_C:
762             {
763                 boolean dateSet = !(
764                     calendar.get(Calendar.DAY_OF_YEAR) == 0 &&
765                     calendar.get(Calendar.YEAR) == 0);
766                 boolean timeSet = !(
767                     calendar.get(Calendar.SECOND) == 0 &&
768                     calendar.get(Calendar.MINUTE) == 0 &&
769                     calendar.get(Calendar.HOUR) == 0);
770                 if (dateSet) {
771                     format(FORMAT_DDDDD, calendar, buf);
772                 }
773                 if (dateSet && timeSet) {
774                     buf.append(' ');
775                 }
776                 if (timeSet) {
777                     format(FORMAT_TTTTT, calendar, buf);
778                 }
779                 break;
780             }
781             case FORMAT_D:
782             {
783                 int d = calendar.get(Calendar.DAY_OF_MONTH);
784                 buf.append(d);
785                 break;
786             }
787             case FORMAT_DD:
788             {
789                 int d = calendar.get(Calendar.DAY_OF_MONTH);
790                 if (d < 10) {
791                     buf.append('0');
792                 }
793                 buf.append(d);
794                 break;
795             }
796             case FORMAT_DDD:
797             {
798                 int dow = calendar.get(Calendar.DAY_OF_WEEK);
799                 buf.append(locale.daysOfWeekShort[dow]); // e.g. Sun
800
break;
801             }
802             case FORMAT_DDDD:
803             {
804                 int dow = calendar.get(Calendar.DAY_OF_WEEK);
805                 buf.append(locale.daysOfWeekLong[dow]); // e.g. Sunday
806
break;
807             }
808             case FORMAT_DDDDD:
809             {
810                 // Officially, we should use the system's short date
811
// format. But for now, we always print using the default
812
// format, m/d/yy.
813
format(FORMAT_M, calendar,buf);
814                 buf.append(locale.dateSeparator);
815                 format(FORMAT_D, calendar,buf);
816                 buf.append(locale.dateSeparator);
817                 format(FORMAT_YY, calendar,buf);
818                 break;
819             }
820             case FORMAT_DDDDDD:
821             {
822                 format(FORMAT_MMMM_UPPER, calendar, buf);
823                 buf.append(" ");
824                 format(FORMAT_DD, calendar, buf);
825                 buf.append(", ");
826                 format(FORMAT_YYYY, calendar, buf);
827                 break;
828             }
829             case FORMAT_W:
830             {
831                 int dow = calendar.get(Calendar.DAY_OF_WEEK);
832                 buf.append(dow);
833                 break;
834             }
835             case FORMAT_WW:
836             {
837                 int woy = calendar.get(Calendar.WEEK_OF_YEAR);
838                 buf.append(woy);
839                 break;
840             }
841             case FORMAT_M:
842             {
843                 int m = calendar.get(Calendar.MONTH) + 1; // 0-based
844
buf.append(m);
845                 break;
846             }
847             case FORMAT_MM:
848             {
849                 int mm = calendar.get(Calendar.MONTH) + 1; // 0-based
850
if (mm < 10) {
851                     buf.append('0');
852                 }
853                 buf.append(mm);
854                 break;
855             }
856             case FORMAT_MMM_LOWER:
857             case FORMAT_MMM_UPPER:
858             {
859                 int m = calendar.get(Calendar.MONTH); // 0-based
860
buf.append(locale.monthsShort[m]); // e.g. Jan
861
break;
862             }
863             case FORMAT_MMMM_LOWER:
864             case FORMAT_MMMM_UPPER:
865             {
866                 int m = calendar.get(Calendar.MONTH); // 0-based
867
buf.append(locale.monthsLong[m]); // e.g. January
868
break;
869             }
870             case FORMAT_Q:
871             {
872                 int m = calendar.get(Calendar.MONTH);
873                 // 0(Jan) -> q1, 1(Feb) -> q1, 2(Mar) -> q1, 3(Apr) -> q2
874
int q = m / 3 + 1;
875                 buf.append(q);
876                 break;
877             }
878             case FORMAT_Y:
879             {
880                 int doy = calendar.get(Calendar.DAY_OF_YEAR);
881                 buf.append(doy);
882                 break;
883             }
884             case FORMAT_YY:
885             {
886                 int y = calendar.get(Calendar.YEAR) % 100;
887                 if (y < 10) {
888                     buf.append('0');
889                 }
890                 buf.append(y);
891                 break;
892             }
893             case FORMAT_YYYY:
894             {
895                 int y = calendar.get(Calendar.YEAR);
896                 buf.append(y);
897                 break;
898             }
899             case FORMAT_H:
900             {
901                 int h = calendar.get(
902                     twelveHourClock ? Calendar.HOUR : Calendar.HOUR_OF_DAY);
903                 buf.append(h);
904                 break;
905             }
906             case FORMAT_HH:
907             {
908                 int h = calendar.get(
909                     twelveHourClock ? Calendar.HOUR : Calendar.HOUR_OF_DAY);
910                 if (h < 10) {
911                     buf.append('0');
912                 }
913                 buf.append(h);
914                 break;
915             }
916             case FORMAT_N:
917             {
918                 int n = calendar.get(Calendar.MINUTE);
919                 buf.append(n);
920                 break;
921             }
922             case FORMAT_NN:
923             {
924                 int n = calendar.get(Calendar.MINUTE);
925                 if (n < 10) {
926                     buf.append('0');
927                 }
928                 buf.append(n);
929                 break;
930             }
931             case FORMAT_S:
932             {
933                 int s = calendar.get(Calendar.SECOND);
934                 buf.append(s);
935                 break;
936             }
937             case FORMAT_SS:
938             {
939                 int s = calendar.get(Calendar.SECOND);
940                 if (s < 10) {
941                     buf.append('0');
942                 }
943                 buf.append(s);
944                 break;
945             }
946             case FORMAT_TTTTT:
947             {
948                 // Officially, we should use the system's time format. But
949
// for now, we always print using the default format, h:mm:ss.
950
format(FORMAT_H, calendar,buf);
951                 buf.append(locale.timeSeparator);
952                 format(FORMAT_NN, calendar,buf);
953                 buf.append(locale.timeSeparator);
954                 format(FORMAT_SS, calendar,buf);
955                 break;
956             }
957             case FORMAT_AMPM:
958             case FORMAT_UPPER_AM_SOLIDUS_PM:
959             {
960                 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM;
961                 buf.append(isAm ? "AM" : "PM");
962                 break;
963             }
964             case FORMAT_LOWER_AM_SOLIDUS_PM:
965             {
966                 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM;
967                 buf.append(isAm ? "am" : "pm");
968                 break;
969             }
970             case FORMAT_UPPER_A_SOLIDUS_P:
971             {
972                 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM;
973                 buf.append(isAm ? "A" : "P");
974                 break;
975             }
976             case FORMAT_LOWER_A_SOLIDUS_P:
977             {
978                 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM;
979                 buf.append(isAm ? "a" : "p");
980                 break;
981             }
982             default:
983                 throw new Error JavaDoc();
984             }
985         }
986     }
987
988     /**
989      * A FormatLocale contains all information necessary to format objects
990      * based upon the locale of the end-user. Use {@link Format#createLocale}
991      * to make one.
992      */

993     public static class FormatLocale
994     {
995         char thousandSeparator;
996         char decimalPlaceholder;
997         String JavaDoc dateSeparator;
998         String JavaDoc timeSeparator;
999         String JavaDoc currencySymbol;
1000        String JavaDoc currencyFormat;
1001        String JavaDoc[] daysOfWeekShort;
1002        String JavaDoc[] daysOfWeekLong;
1003        String JavaDoc[] monthsShort;
1004        String JavaDoc[] monthsLong;
1005        private final Locale locale;
1006
1007        private FormatLocale(
1008                char thousandSeparator,
1009                char decimalPlaceholder,
1010                String JavaDoc dateSeparator,
1011                String JavaDoc timeSeparator,
1012                String JavaDoc currencySymbol,
1013                String JavaDoc currencyFormat,
1014                String JavaDoc[] daysOfWeekShort,
1015                String JavaDoc[] daysOfWeekLong,
1016                String JavaDoc[] monthsShort,
1017                String JavaDoc[] monthsLong,
1018                Locale locale)
1019        {
1020            this.locale = locale;
1021            if (thousandSeparator == '\0') {
1022                thousandSeparator = thousandSeparator_en;
1023            }
1024            this.thousandSeparator = thousandSeparator;
1025            if (decimalPlaceholder == '\0') {
1026                decimalPlaceholder = decimalPlaceholder_en;
1027            }
1028            this.decimalPlaceholder = decimalPlaceholder;
1029            if (dateSeparator == null) {
1030                dateSeparator = dateSeparator_en;
1031            }
1032            this.dateSeparator = dateSeparator;
1033            if (timeSeparator == null) {
1034                timeSeparator = timeSeparator_en;
1035            }
1036            this.timeSeparator = timeSeparator;
1037            if (currencySymbol == null) {
1038                currencySymbol = currencySymbol_en;
1039            }
1040            this.currencySymbol = currencySymbol;
1041            if (currencyFormat == null) {
1042                currencyFormat = currencyFormat_en;
1043            }
1044            this.currencyFormat = currencyFormat;
1045            if (daysOfWeekShort == null) {
1046                daysOfWeekShort = daysOfWeekShort_en;
1047            }
1048            this.daysOfWeekShort = daysOfWeekShort;
1049            if (daysOfWeekLong == null) {
1050                daysOfWeekLong = daysOfWeekLong_en;
1051            }
1052            this.daysOfWeekLong = daysOfWeekLong;
1053            if (monthsShort == null) {
1054                monthsShort = monthsShort_en;
1055            }
1056            this.monthsShort = monthsShort;
1057            if (monthsLong == null) {
1058                monthsLong = monthsLong_en;
1059            }
1060            this.monthsLong = monthsLong;
1061            if (daysOfWeekShort.length != 8 ||
1062                daysOfWeekLong.length != 8 ||
1063                monthsShort.length != 13 ||
1064                monthsLong.length != 13) {
1065                throw new IllegalArgumentException JavaDoc(
1066                    "Format: day or month array has incorrect length");
1067            }
1068        }
1069
1070// /**
1071
// * Get the localized string for day of week, given
1072
// * an <CODE>int</CODE> day value, with 0 = SUNDAY.
1073
// */
1074
// public static String getDayOfWeek( int day )
1075
// {
1076
// LocaleResource localeResource = FormatLocale.getResource();
1077
// switch ( day )
1078
// {
1079
// case 0: return localeResource.getsunday();
1080
// case 1: return localeResource.getmonday();
1081
// case 2: return localeResource.gettuesday();
1082
// case 3: return localeResource.getwednesday();
1083
// case 4: return localeResource.getthursday();
1084
// case 5: return localeResource.getfriday();
1085
// case 6: return localeResource.getsaturday();
1086
// default: throw new IllegalArgumentException();
1087
// }
1088
// }
1089

1090// /**
1091
// * Get the localized string for month of year, given
1092
// * an <CODE>int</CODE> month value, with 0 = JANUARY.
1093
// */
1094
// public static String getMonthOfYear( int month )
1095
// {
1096
// LocaleResource localeResource = FormatLocale.getResource();
1097
// switch ( month )
1098
// {
1099
// case 0: return localeResource.getjanuary();
1100
// case 1: return localeResource.getfebruary();
1101
// case 2: return localeResource.getmarch();
1102
// case 3: return localeResource.getapril();
1103
// case 4: return localeResource.getmay();
1104
// case 5: return localeResource.getjune();
1105
// case 6: return localeResource.getjuly();
1106
// case 7: return localeResource.getaugust();
1107
// case 8: return localeResource.getseptember();
1108
// case 9: return localeResource.getoctober();
1109
// case 10: return localeResource.getnovember();
1110
// case 11: return localeResource.getdecember();
1111
// default: throw new IllegalArgumentException();
1112
// }
1113
// }
1114

1115// /**
1116
// * Get the string representation of the calendar
1117
// * quarter for a given quarter and year. Subclasses
1118
// * should override this method.
1119
// */
1120
// public static String getCalendarQuarter( int quarterIn, int yearIn )
1121
// {
1122
// Integer year = new Integer (yearIn % 100 );
1123
// Integer quarter = new Integer( quarterIn );
1124

1125// String strYear = ( year.intValue() < 10 )
1126
// ? "0" + year.toString() : year.toString();
1127
// LocaleResource localeResource = FormatLocale.getResource();
1128
// String ret = localeResource.getcalendarQuarter(quarter.toString(), strYear);
1129

1130// return ret;
1131
// }
1132

1133// /**
1134
// * Get the string representation of the fiscal
1135
// * quarter for a given quarter and year. Subclasses
1136
// * should override this method.
1137
// */
1138
// public static String getFiscalQuarter( int quarterIn, int yearIn )
1139
// {
1140
// Integer year = new Integer (yearIn % 100 );
1141
// Integer quarter = new Integer( quarterIn );
1142

1143// String strYear = ( year.intValue() < 10 )
1144
// ? "0" + year.toString() : year.toString();
1145

1146// LocaleResource localeResource = FormatLocale.getResource();
1147
// String ret = localeResource.getfiscalQuarter(quarter.toString(),
1148
// strYear);
1149
// return ret;
1150
// }
1151

1152    }
1153
1154    private static class StringFormat extends BasicFormat
1155    {
1156        int stringCase;
1157
1158        StringFormat(int stringCase) {
1159            this.stringCase = stringCase;
1160        }
1161    }
1162
1163    /** Values for {@link StringFormat#stringCase}. */
1164    private static final int CASE_ASIS = 0;
1165    private static final int CASE_UPPER = 1;
1166    private static final int CASE_LOWER = 2;
1167
1168    /** Types of Format. */
1169    private static final int GENERAL = 0;
1170    private static final int DATE = 3;
1171    private static final int NUMERIC = 4;
1172    private static final int STRING = 5;
1173    /** A Format is flagged SPECIAL if it needs special processing
1174     * during parsing. */

1175    private static final int SPECIAL = 8;
1176
1177    /** Values for {@link Format.BasicFormat#code}. */
1178    private static final int FORMAT_NULL = 0;
1179    private static final int FORMAT_C = 3;
1180    private static final int FORMAT_D = 4;
1181    private static final int FORMAT_DD = 5;
1182    private static final int FORMAT_DDD = 6;
1183    private static final int FORMAT_DDDD = 7;
1184    private static final int FORMAT_DDDDD = 8;
1185    private static final int FORMAT_DDDDDD = 9;
1186    private static final int FORMAT_W = 10;
1187    private static final int FORMAT_WW = 11;
1188    private static final int FORMAT_M = 12;
1189    private static final int FORMAT_MM = 13;
1190    private static final int FORMAT_MMM_UPPER = 14;
1191    private static final int FORMAT_MMMM_UPPER = 15;
1192    private static final int FORMAT_Q = 16;
1193    private static final int FORMAT_Y = 17;
1194    private static final int FORMAT_YY = 18;
1195    private static final int FORMAT_YYYY = 19;
1196    private static final int FORMAT_H = 20;
1197    private static final int FORMAT_HH = 21;
1198    private static final int FORMAT_N = 22;
1199    private static final int FORMAT_NN = 23;
1200    private static final int FORMAT_S = 24;
1201    private static final int FORMAT_SS = 25;
1202    private static final int FORMAT_TTTTT = 26;
1203    private static final int FORMAT_UPPER_AM_SOLIDUS_PM = 27;
1204    private static final int FORMAT_LOWER_AM_SOLIDUS_PM = 28;
1205    private static final int FORMAT_UPPER_A_SOLIDUS_P = 29;
1206    private static final int FORMAT_LOWER_A_SOLIDUS_P = 30;
1207    private static final int FORMAT_AMPM = 31;
1208    private static final int FORMAT_0 = 32;
1209    private static final int FORMAT_POUND = 33;
1210    private static final int FORMAT_DECIMAL = 34;
1211    private static final int FORMAT_PERCENT = 35;
1212    private static final int FORMAT_THOUSEP = 36;
1213    private static final int FORMAT_TIMESEP = 37;
1214    private static final int FORMAT_DATESEP = 38;
1215    private static final int FORMAT_E_MINUS_UPPER = 39;
1216    private static final int FORMAT_E_PLUS_UPPER = 40;
1217    private static final int FORMAT_E_MINUS_LOWER = 41;
1218    private static final int FORMAT_E_PLUS_LOWER = 42;
1219    private static final int FORMAT_LITERAL = 43;
1220    private static final int FORMAT_BACKSLASH = 44;
1221    private static final int FORMAT_QUOTE = 45;
1222    private static final int FORMAT_CHARACTER_OR_SPACE = 46;
1223    private static final int FORMAT_CHARACTER_OR_NOTHING = 47;
1224    private static final int FORMAT_LOWER = 48;
1225    private static final int FORMAT_UPPER = 49;
1226    private static final int FORMAT_FILL_FROM_LEFT = 50;
1227    private static final int FORMAT_SEMI = 51;
1228    private static final int FORMAT_GENERAL_NUMBER = 52;
1229    private static final int FORMAT_GENERAL_DATE = 53;
1230    private static final int FORMAT_INTL_CURRENCY = 54;
1231    private static final int FORMAT_MMM_LOWER = 55;
1232    private static final int FORMAT_MMMM_LOWER = 56;
1233    private static final int FORMAT_USD = 57;
1234
1235    private static final Token nfe(
1236        int code, int flags, String JavaDoc token, String JavaDoc purpose, String JavaDoc description)
1237    {
1238        Util.discard(purpose);
1239        Util.discard(description);
1240        return new Token(code,flags,token);
1241    }
1242
1243    public static final List<Token> getTokenList()
1244    {
1245        return Collections.unmodifiableList(Arrays.asList(tokens));
1246    }
1247
1248    static final Token[] tokens = {
1249        nfe(FORMAT_NULL , NUMERIC, null, "No formatting", "Display the number with no formatting."),
1250        nfe(FORMAT_C , DATE, "C", null, "Display the date as ddddd and display the time as t t t t t, in that order. Display only date information if there is no fractional part to the date serial number; display only time information if there is no integer portion."),
1251        nfe(FORMAT_D , DATE, "d", null, "Display the day as a number without a leading zero (1 - 31)."),
1252        nfe(FORMAT_DD , DATE, "dd", null, "Display the day as a number with a leading zero (01 - 31)."),
1253        nfe(FORMAT_DDD , DATE, "Ddd", null, "Display the day as an abbreviation (Sun - Sat)."),
1254        nfe(FORMAT_DDDD , DATE, "dddd", null, "Display the day as a full name (Sunday - Saturday)."),
1255        nfe(FORMAT_DDDDD , DATE, "ddddd", null, "Display the date as a complete date (including day, month, and year), formatted according to your system's short date format setting. The default short date format is m/d/yy."),
1256        nfe(FORMAT_DDDDDD , DATE, "dddddd", null, "Display a date serial number as a complete date (including day, month, and year) formatted according to the long date setting recognized by your system. The default long date format is mmmm dd, yyyy."),
1257        nfe(FORMAT_W , DATE, "w", null, "Display the day of the week as a number (1 for Sunday through 7 for Saturday)."),
1258        nfe(FORMAT_WW , DATE, "ww", null, "Display the week of the year as a number (1 - 53)."),
1259        nfe(FORMAT_M , DATE | SPECIAL, "m", null, "Display the month as a number without a leading zero (1 - 12). If m immediately follows h or hh, the minute rather than the month is displayed."),
1260        nfe(FORMAT_MM , DATE | SPECIAL, "mm", null, "Display the month as a number with a leading zero (01 - 12). If m immediately follows h or hh, the minute rather than the month is displayed."),
1261        nfe(FORMAT_MMM_LOWER , DATE, "mmm", null, "Display the month as an abbreviation (Jan - Dec)."),
1262        nfe(FORMAT_MMMM_LOWER , DATE, "mmmm", null, "Display the month as a full month name (January - December)."),
1263        nfe(FORMAT_MMM_UPPER , DATE, "mmm", null, "Display the month as an abbreviation (Jan - Dec)."),
1264        nfe(FORMAT_MMMM_UPPER , DATE, "mmmm", null, "Display the month as a full month name (January - December)."),
1265        nfe(FORMAT_Q , DATE, "q", null, "Display the quarter of the year as a number (1 - 4)."),
1266        nfe(FORMAT_Y , DATE, "y", null, "Display the day of the year as a number (1 - 366)."),
1267        nfe(FORMAT_YY , DATE, "yy", null, "Display the year as a 2-digit number (00 - 99)."),
1268        nfe(FORMAT_YYYY , DATE, "yyyy", null, "Display the year as a 4-digit number (100 - 9999)."),
1269        nfe(FORMAT_H , DATE, "h", null, "Display the hour as a number without leading zeros (0 - 23)."),
1270        nfe(FORMAT_HH , DATE, "hh", null, "Display the hour as a number with leading zeros (00 - 23)."),
1271        nfe(FORMAT_N , DATE, "n", null, "Display the minute as a number without leading zeros (0 - 59)."),
1272        nfe(FORMAT_NN , DATE, "nn", null, "Display the minute as a number with leading zeros (00 - 59)."),
1273        nfe(FORMAT_S , DATE, "s", null, "Display the second as a number without leading zeros (0 - 59)."),
1274        nfe(FORMAT_SS , DATE, "ss", null, "Display the second as a number with leading zeros (00 - 59)."),
1275        nfe(FORMAT_TTTTT , DATE, "ttttt", null, "Display a time as a complete time (including hour, minute, and second), formatted using the time separator defined by the time format recognized by your system. A leading zero is displayed if the leading zero option is selected and the time is before 10:00 A.M. or P.M. The default time format is h:mm:ss."),
1276        nfe(FORMAT_UPPER_AM_SOLIDUS_PM , DATE, "AM/PM", null, "Use the 12-hour clock and display an uppercase AM with any hour before noon; display an uppercase PM with any hour between noon and 11:59 P.M."),
1277        nfe(FORMAT_LOWER_AM_SOLIDUS_PM , DATE, "am/pm", null, "Use the 12-hour clock and display a lowercase AM with any hour before noon; display a lowercase PM with any hour between noon and 11:59 P.M."),
1278        nfe(FORMAT_UPPER_A_SOLIDUS_P , DATE, "A/P", null, "Use the 12-hour clock and display an uppercase A with any hour before noon; display an uppercase P with any hour between noon and 11:59 P.M."),
1279        nfe(FORMAT_LOWER_A_SOLIDUS_P , DATE, "a/p", null, "Use the 12-hour clock and display a lowercase A with any hour before noon; display a lowercase P with any hour between noon and 11:59 P.M."),
1280        nfe(FORMAT_AMPM , DATE, "AMPM", null, "Use the 12-hour clock and display the AM string literal as defined by your system with any hour before noon; display the PM string literal as defined by your system with any hour between noon and 11:59 P.M. AMPM can be either uppercase or lowercase, but the case of the string displayed matches the string as defined by your system settings. The default format is AM/PM."),
1281        nfe(FORMAT_0 , NUMERIC | SPECIAL, "0", "Digit placeholder", "Display a digit or a zero. If the expression has a digit in the position where the 0 appears in the format string, display it; otherwise, display a zero in that position. If the number has fewer digits than there are zeros (on either side of the decimal) in the format expression, display leading or trailing zeros. If the number has more digits to the right of the decimal separator than there are zeros to the right of the decimal separator in the format expression, round the number to as many decimal places as there are zeros. If the number has more digits to the left of the decimal separator than there are zeros to the left of the decimal separator in the format expression, display the extra digits without modification."),
1282        nfe(FORMAT_POUND , NUMERIC | SPECIAL, "#", "Digit placeholder", "Display a digit or nothing. If the expression has a digit in the position where the # appears in the format string, display it; otherwise, display nothing in that position. This symbol works like the 0 digit placeholder, except that leading and trailing zeros aren't displayed if the number has the same or fewer digits than there are # characters on either side of the decimal separator in the format expression."),
1283        nfe(FORMAT_DECIMAL , NUMERIC | SPECIAL, ".", "Decimal placeholder", "In some locales, a comma is used as the decimal separator. The decimal placeholder determines how many digits are displayed to the left and right of the decimal separator. If the format expression contains only number signs to the left of this symbol, numbers smaller than 1 begin with a decimal separator. If you always want a leading zero displayed with fractional numbers, use 0 as the first digit placeholder to the left of the decimal separator instead. The actual character used as a decimal placeholder in the formatted output depends on the Number Format recognized by your system."),
1284        nfe(FORMAT_PERCENT , NUMERIC, "%", "Percent placeholder", "The expression is multiplied by 100. The percent character (%) is inserted in the position where it appears in the format string."),
1285        nfe(FORMAT_THOUSEP , NUMERIC | SPECIAL, ",", "Thousand separator", "In some locales, a period is used as a thousand separator. The thousand separator separates thousands from hundreds within a number that has four or more places to the left of the decimal separator. Standard use of the thousand separator is specified if the format contains a thousand separator surrounded by digit placeholders (0 or #). Two adjacent thousand separators or a thousand separator immediately to the left of the decimal separator (whether or not a decimal is specified) means \"scale the number by dividing it by 1000, rounding as needed.\" You can scale large numbers using this technique. For example, you can use the format string \"##0,,\" to represent 100 million as 100. Numbers smaller than 1 million are displayed as 0. Two adjacent thousand separators in any position other than immediately to the left of the decimal separator are treated simply as specifying the use of a thousand separator. The actual character used as the thousand separator in the formatted output depends on the Number Format recognized by your system."),
1286        nfe(FORMAT_TIMESEP , DATE | SPECIAL, ":", "Time separator", "In some locales, other characters may be used to represent the time separator. The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings."),
1287        nfe(FORMAT_DATESEP , DATE | SPECIAL, "/", "Date separator", "In some locales, other characters may be used to represent the date separator. The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings."),
1288        nfe(FORMAT_E_MINUS_UPPER , NUMERIC | SPECIAL, "E-", "Scientific format", "If the format expression contains at least one digit placeholder (0 or #) to the right of E-, E+, e-, or e+, the number is displayed in scientific format and E or e is inserted between the number and its exponent. The number of digit placeholders to the right determines the number of digits in the exponent. Use E- or e- to place a minus sign next to negative exponents. Use E+ or e+ to place a minus sign next to negative exponents and a plus sign next to positive exponents."),
1289        nfe(FORMAT_E_PLUS_UPPER , NUMERIC | SPECIAL, "E+", "Scientific format", "See E-."),
1290        nfe(FORMAT_E_MINUS_LOWER , NUMERIC | SPECIAL, "e-", "Scientific format", "See E-."),
1291        nfe(FORMAT_E_PLUS_LOWER , NUMERIC | SPECIAL, "e+", "Scientific format", "See E-."),
1292        nfe(FORMAT_LITERAL , GENERAL, "-", "Display a literal character", "To display a character other than one of those listed, precede it with a backslash (\\) or enclose it in double quotation marks (\" \")."),
1293        nfe(FORMAT_LITERAL , GENERAL, "+", "Display a literal character", "See -."),
1294        nfe(FORMAT_LITERAL , GENERAL, "$", "Display a literal character", "See -."),
1295        nfe(FORMAT_LITERAL , GENERAL, "(", "Display a literal character", "See -."),
1296        nfe(FORMAT_LITERAL , GENERAL, ")", "Display a literal character", "See -."),
1297        nfe(FORMAT_LITERAL , GENERAL, " ", "Display a literal character", "See -."),
1298        nfe(FORMAT_BACKSLASH , GENERAL | SPECIAL, "\\", "Display the next character in the format string", "Many characters in the format expression have a special meaning and can't be displayed as literal characters unless they are preceded by a backslash. The backslash itself isn't displayed. Using a backslash is the same as enclosing the next character in double quotation marks. To display a backslash, use two backslashes (\\). Examples of characters that can't be displayed as literal characters are the date- and time-formatting characters (a, c, d, h, m, n, p, q, s, t, w, y, and /:), the numeric-formatting characters (#, 0, %, E, e, comma, and period), and the string-formatting characters (@, &, <, >, and !)."),
1299        nfe(FORMAT_QUOTE , GENERAL | SPECIAL, "\"", "Display the string inside the double quotation marks", "To include a string in format from within code, you must use Chr(34) to enclose the text (34 is the character code for a double quotation mark)."),
1300        nfe(FORMAT_CHARACTER_OR_SPACE , STRING, "@", "Character placeholder", "Display a character or a space. If the string has a character in the position where the @ appears in the format string, display it; otherwise, display a space in that position. Placeholders are filled from right to left unless there is an ! character in the format string. See below."),
1301        nfe(FORMAT_CHARACTER_OR_NOTHING, STRING, "&", "Character placeholder", "Display a character or nothing. If the string has a character in the position where the & appears, display it; otherwise, display nothing. Placeholders are filled from right to left unless there is an ! character in the format string. See below."),
1302        nfe(FORMAT_LOWER , STRING | SPECIAL, "<", "Force lowercase", "Display all characters in lowercase format."),
1303        nfe(FORMAT_UPPER , STRING | SPECIAL, ">", "Force uppercase", "Display all characters in uppercase format."),
1304        nfe(FORMAT_FILL_FROM_LEFT , STRING | SPECIAL, "!", "Force left to right fill of placeholders", "The default is to fill from right to left."),
1305        nfe(FORMAT_SEMI , GENERAL | SPECIAL, ";", "Separates format strings for different kinds of values", "If there is one section, the format expression applies to all values. If there are two sections, the first section applies to positive values and zeros, the second to negative values. If there are three sections, the first section applies to positive values, the second to negative values, and the third to zeros. If there are four sections, the first section applies to positive values, the second to negative values, the third to zeros, and the fourth to Null values."),
1306        nfe(FORMAT_INTL_CURRENCY , NUMERIC | SPECIAL, intlCurrencySymbol + "", null, "Display the locale's currency symbol."),
1307        nfe(FORMAT_USD , GENERAL, "USD", null, "Display USD (U.S. Dollars)."),
1308        nfe(FORMAT_GENERAL_NUMBER , NUMERIC | SPECIAL, "General Number", null, "Shows numbers as entered."),
1309        nfe(FORMAT_GENERAL_DATE , DATE | SPECIAL, "General Date", null, "Shows date and time if expression contains both. If expression is only a date or a time, the missing information is not displayed."),
1310    };
1311
1312    static class MacroToken {
1313        String JavaDoc name;
1314        String JavaDoc translation;
1315        String JavaDoc description;
1316
1317        MacroToken(String JavaDoc name, String JavaDoc translation, String JavaDoc description)
1318        {
1319            this.name = name;
1320            this.translation = translation;
1321            this.description = description;
1322        }
1323    }
1324
1325    // Named formats. todo: Supply the translation strings.
1326
private static final MacroToken[] macroTokens = {
1327        new MacroToken(
1328            "Currency", null, "Shows currency values according to the locale's CurrencyFormat. Negative numbers are inside parentheses."),
1329        new MacroToken(
1330            "Fixed", "0", "Shows at least one digit."),
1331        new MacroToken(
1332            "Standard", "#,##0", "Uses a thousands separator."),
1333        new MacroToken(
1334            "Percent", "0.00%", "Multiplies the value by 100 with a percent sign at the end."),
1335        new MacroToken(
1336            "Scientific", "0.00e+00", "Uses standard scientific notation."),
1337        new MacroToken(
1338            "Long Date", "dddd, mmmm dd, yyyy", "Uses the Long Date format specified in the Regional Settings dialog box of the Microsoft Windows Control Panel."),
1339        new MacroToken(
1340            "Medium Date", "dd-mmm-yy", "Uses the dd-mmm-yy format (for example, 03-Apr-93)"),
1341        new MacroToken(
1342            "Short Date", "m/d/yy", "Uses the Short Date format specified in the Regional Settings dialog box of the Windows Control Panel."),
1343        new MacroToken(
1344            "Long Time", "h:mm:ss AM/PM", "Shows the hour, minute, second, and \"AM\" or \"PM\" using the h:mm:ss format."),
1345        new MacroToken(
1346            "Medium Time", "h:mm AM/PM", "Shows the hour, minute, and \"AM\" or \"PM\" using the \"hh:mm AM/PM\" format."),
1347        new MacroToken(
1348            "Short Time", "hh:mm", "Shows the hour and minute using the hh:mm format."),
1349        new MacroToken(
1350            "Yes/No", "\\Y\\e\\s;\\Y\\e\\s;\\N\\o;\\N\\o", "Any nonzero numeric value (usually - 1) is Yes. Zero is No."),
1351        new MacroToken(
1352            "True/False", "\\T\\r\\u\\e;\\T\\r\\u\\e;\\F\\a\\l\\s\\e;\\F\\a\\l\\s\\e", "Any nonzero numeric value (usually - 1) is True. Zero is False."),
1353        new MacroToken(
1354            "On/Off", "\\O\\n;\\O\\n;\\O\\f\\f;\\O\\f\\f", "Any nonzero numeric value (usually - 1) is On. Zero is Off."),
1355
1356    };
1357
1358    /**
1359     * Constructs a <code>Format</code> in a specific locale.
1360     *
1361     * @param formatString the format string; see
1362     * <a HREF="http://www.apostate.com/programming/vb-format.html">this
1363     * description</a> for more details
1364     * @param locale The locale
1365     */

1366    public Format(String JavaDoc formatString, Locale locale)
1367    {
1368        this(formatString, getBestFormatLocale(locale));
1369    }
1370
1371    /**
1372     * Constructs a <code>Format</code> in a specific locale.
1373     *
1374     * @see FormatLocale
1375     * @see #createLocale
1376     */

1377    public Format(String JavaDoc formatString, FormatLocale locale)
1378    {
1379        if (formatString == null) {
1380            formatString = "";
1381        }
1382        this.formatString = formatString;
1383        if (locale == null) {
1384            locale = locale_US;
1385        }
1386        this.locale = locale;
1387
1388        List<BasicFormat> alternateFormatList = new ArrayList<BasicFormat>();
1389        while (formatString.length() > 0) {
1390            formatString = parseFormatString(
1391                formatString, alternateFormatList);
1392        }
1393
1394        // If the format string is empty, use a Java format.
1395
// Later entries in the formats list default to the first (e.g.
1396
// "#.00;;Nil"), but the first entry must be set.
1397
if (alternateFormatList.size() == 0 || alternateFormatList.get(0) == null) {
1398            format = new JavaFormat(locale.locale);
1399        } else if (alternateFormatList.size() == 1) {
1400            format = (BasicFormat) alternateFormatList.get(0);
1401        } else {
1402            BasicFormat[] alternateFormats =
1403                    (BasicFormat[]) alternateFormatList.toArray(
1404                            new BasicFormat[alternateFormatList.size()]);
1405            format = new AlternateFormat(alternateFormats);
1406        }
1407    }
1408
1409    /**
1410     * Constructs a <code>Format</code> in a specific locale, or retrieves
1411     * one from the cache if one already exists.
1412     *
1413     * <p>If the number of entries in the cache exceeds {@link #CacheLimit},
1414     * replaces the eldest entry in the cache.
1415     *
1416     * @param formatString the format string; see
1417     * <a HREF="http://www.apostate.com/programming/vb-format.html">this
1418     * description</a> for more details
1419     */

1420    public static Format get(String JavaDoc formatString, Locale locale) {
1421        String JavaDoc key = formatString + "@@@" + locale;
1422        Format format = (Format) cache.get(key);
1423        if (format == null) {
1424            synchronized (cache) {
1425                format = (Format) cache.get(key);
1426                if (format == null) {
1427                    format = new Format(formatString, locale);
1428                    cache.put(key, format);
1429                }
1430            }
1431        }
1432        return format;
1433    }
1434
1435    /**
1436     * Create a {@link FormatLocale} object characterized by the given
1437     * properties.
1438     *
1439     * @param thousandSeparator the character used to separate thousands in
1440     * numbers, or ',' by default. For example, 12345 is '12,345 in English,
1441     * '12.345 in French.
1442     * @param decimalPlaceholder the character placed between the integer and
1443     * the fractional part of decimal numbers, or '.' by default. For
1444     * example, 12.34 is '12.34' in English, '12,34' in French.
1445     * @param dateSeparator the character placed between the year, month and
1446     * day of a date such as '12/07/2001', or '/' by default.
1447     * @param timeSeparator the character placed between the hour, minute and
1448     * second value of a time such as '1:23:45 AM', or ':' by default.
1449     * @param daysOfWeekShort Short forms of the days of the week.
1450     * The array is 1-based, because position
1451     * {@link Calendar#SUNDAY} (= 1) must hold Sunday, etc.
1452     * The array must have 8 elements.
1453     * For example {"", "Sun", "Mon", ..., "Sat"}.
1454     * @param daysOfWeekLong Long forms of the days of the week.
1455     * The array is 1-based, because position
1456     * {@link Calendar#SUNDAY} must hold Sunday, etc.
1457     * The array must have 8 elements.
1458     * For example {"", "Sunday", ..., "Saturday"}.
1459     * @param monthsShort Short forms of the months of the year.
1460     * The array is 0-based, because position
1461     * {@link Calendar#JANUARY} (= 0) holds January, etc.
1462     * For example {"Jan", ..., "Dec", ""}.
1463     * @param monthsLong Long forms of the months of the year.
1464     * The array is 0-based, because position
1465     * {@link Calendar#JANUARY} (= 0) holds January, etc.
1466     * For example {"January", ..., "December", ""}.
1467     * @param locale if this is not null, register that the constructed
1468     * <code>FormatLocale</code> is the default for <code>locale</code>
1469     */

1470    public static FormatLocale createLocale(
1471        char thousandSeparator,
1472        char decimalPlaceholder,
1473        String JavaDoc dateSeparator,
1474        String JavaDoc timeSeparator,
1475        String JavaDoc currencySymbol,
1476        String JavaDoc currencyFormat,
1477        String JavaDoc[] daysOfWeekShort,
1478        String JavaDoc[] daysOfWeekLong,
1479        String JavaDoc[] monthsShort,
1480        String JavaDoc[] monthsLong,
1481        Locale locale)
1482    {
1483        FormatLocale formatLocale = new FormatLocale(
1484            thousandSeparator, decimalPlaceholder, dateSeparator,
1485            timeSeparator, currencySymbol, currencyFormat, daysOfWeekShort,
1486            daysOfWeekLong, monthsShort, monthsLong, locale);
1487        if (locale != null) {
1488            registerFormatLocale(formatLocale, locale);
1489        }
1490        return formatLocale;
1491    }
1492
1493    public static FormatLocale createLocale(Locale locale)
1494    {
1495        final DecimalFormatSymbols decimalSymbols =
1496                new DecimalFormatSymbols(locale);
1497        final DateFormatSymbols dateSymbols = new DateFormatSymbols(locale);
1498
1499        Calendar calendar = Calendar.getInstance(locale);
1500        calendar.set(1969, 11, 31, 0, 0, 0);
1501        final Date date = calendar.getTime();
1502
1503        final java.text.DateFormat JavaDoc dateFormat =
1504                java.text.DateFormat.getDateInstance(
1505                        java.text.DateFormat.SHORT, locale);
1506        final String JavaDoc dateValue = dateFormat.format(date); // "12/31/69"
1507
String JavaDoc dateSeparator = dateValue.substring(2, 3); // "/"
1508

1509        final java.text.DateFormat JavaDoc timeFormat =
1510                java.text.DateFormat.getTimeInstance(
1511                        java.text.DateFormat.SHORT, locale);
1512        final String JavaDoc timeValue = timeFormat.format(date); // "12:00:00"
1513
String JavaDoc timeSeparator = timeValue.substring(2, 3); // ":"
1514

1515        // Deduce the locale's currency format.
1516
// For example, US is "$#,###.00"; France is "#,###-00FF".
1517
final NumberFormat currencyFormat =
1518                NumberFormat.getCurrencyInstance(locale);
1519        final String JavaDoc currencyValue = currencyFormat.format(123456.78);
1520        String JavaDoc currencyLeft =
1521                currencyValue.substring(0, currencyValue.indexOf("1"));
1522        String JavaDoc currencyRight =
1523                currencyValue.substring(currencyValue.indexOf("8") + 1);
1524        StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
1525        buf.append(currencyLeft);
1526        int minimumIntegerDigits = currencyFormat.getMinimumIntegerDigits();
1527        for (int i = Math.max(minimumIntegerDigits, 4) - 1; i >= 0; --i) {
1528            buf.append(i < minimumIntegerDigits ? '0' : '#');
1529            if (i % 3 == 0 && i > 0) {
1530                buf.append(',');
1531            }
1532        }
1533        if (currencyFormat.getMaximumFractionDigits() > 0) {
1534            buf.append('.');
1535            appendTimes(buf, '0', currencyFormat.getMinimumFractionDigits());
1536            appendTimes(buf, '#',
1537                    currencyFormat.getMaximumFractionDigits() -
1538                    currencyFormat.getMinimumFractionDigits());
1539        }
1540        buf.append(currencyRight);
1541        String JavaDoc currencyFormatString = buf.toString();
1542
1543        return createLocale(
1544                decimalSymbols.getGroupingSeparator(),
1545                decimalSymbols.getDecimalSeparator(),
1546                dateSeparator,
1547                timeSeparator,
1548                decimalSymbols.getCurrencySymbol(),
1549                currencyFormatString,
1550                dateSymbols.getShortWeekdays(),
1551                dateSymbols.getWeekdays(),
1552                dateSymbols.getShortMonths(),
1553                dateSymbols.getMonths(),
1554                locale);
1555    }
1556
1557    private static void appendTimes(StringBuilder JavaDoc buf, char c, int i) {
1558        while (i-- > 0) {
1559            buf.append(c);
1560        }
1561    }
1562
1563    /**
1564     * Returns the {@link FormatLocale} which precisely matches {@link Locale},
1565     * if any, or null if there is none.
1566     */

1567    public static FormatLocale getFormatLocale(Locale locale)
1568    {
1569        if (locale == null) {
1570            locale = Locale.US;
1571        }
1572        String JavaDoc key = locale.toString();
1573        return mapLocaleToFormatLocale.get(key);
1574    }
1575
1576    /**
1577     * Returns the best {@link FormatLocale} for a given {@link Locale}.
1578     * Never returns null, even if <code>locale</code> is null.
1579     */

1580    public static synchronized FormatLocale getBestFormatLocale(Locale locale)
1581    {
1582        FormatLocale formatLocale;
1583        if (locale == null) {
1584            return locale_US;
1585        }
1586        String JavaDoc key = locale.toString();
1587        // Look in the cache first.
1588
formatLocale = mapLocaleToFormatLocale.get(key);
1589        if (formatLocale == null) {
1590            // Not in the cache, so ask the factory.
1591
formatLocale = getFormatLocaleUsingFactory(locale);
1592            if (formatLocale == null) {
1593                formatLocale = locale_US;
1594            }
1595            // Add to cache.
1596
mapLocaleToFormatLocale.put(key, formatLocale);
1597        }
1598        return formatLocale;
1599    }
1600
1601    private static FormatLocale getFormatLocaleUsingFactory(Locale locale)
1602    {
1603        FormatLocale formatLocale;
1604        // Lookup full locale, e.g. "en-US-Boston"
1605
if (!locale.getVariant().equals("")) {
1606            formatLocale = createLocale(locale);
1607            if (formatLocale != null) {
1608                return formatLocale;
1609            }
1610            locale = new Locale(locale.getLanguage(), locale.getCountry());
1611        }
1612        // Lookup language and country, e.g. "en-US"
1613
if (!locale.getCountry().equals("")) {
1614            formatLocale = createLocale(locale);
1615            if (formatLocale != null) {
1616                return formatLocale;
1617            }
1618            locale = new Locale(locale.getLanguage());
1619        }
1620        // Lookup language, e.g. "en"
1621
formatLocale = createLocale(locale);
1622        if (formatLocale != null) {
1623            return formatLocale;
1624        }
1625        return null;
1626    }
1627
1628    /**
1629     * Registers a {@link FormatLocale} to a given {@link Locale}. Returns the
1630     * previous mapping.
1631     */

1632    public static FormatLocale registerFormatLocale(
1633        FormatLocale formatLocale, Locale locale)
1634    {
1635        String JavaDoc key = locale.toString(); // e.g. "en_us_Boston"
1636
FormatLocale previous = mapLocaleToFormatLocale.put(key, formatLocale);
1637        return previous;
1638    }
1639
1640    // Values for variable numberState below.
1641
static final int NOT_IN_A_NUMBER = 0;
1642    static final int LEFT_OF_POINT = 1;
1643    static final int RIGHT_OF_POINT = 2;
1644    static final int RIGHT_OF_EXP = 3;
1645
1646    /**
1647     * Reads formatString up to the first semi-colon, or to the end if there
1648     * are no semi-colons. Adds a format to alternateFormatList, and returns
1649     * the remains of formatString.
1650     */

1651    private String JavaDoc parseFormatString(
1652        String JavaDoc formatString, List<BasicFormat> alternateFormatList)
1653    {
1654        // Where we are in a numeric format.
1655
int numberState = NOT_IN_A_NUMBER;
1656        StringBuilder JavaDoc ignored = new StringBuilder JavaDoc();
1657        String JavaDoc prevIgnored = null;
1658        boolean haveSeenNumber = false;
1659        int digitsLeftOfPoint = 0,
1660            digitsRightOfPoint = 0,
1661            digitsRightOfExp = 0,
1662            zeroesLeftOfPoint = 0,
1663            zeroesRightOfPoint = 0,
1664            zeroesRightOfExp = 0;
1665        int stringCase = CASE_ASIS;
1666        boolean useDecimal = false,
1667                useThouSep = false,
1668                fillFromRight = true;
1669
1670        /** Whether to print numbers in decimal or exponential format. Valid
1671         * values are FORMAT_NULL, FORMAT_E_PLUS_LOWER, FORMAT_E_MINUS_LOWER,
1672         * FORMAT_E_PLUS_UPPER, FORMAT_E_MINUS_UPPER. */

1673        int expFormat = FORMAT_NULL;
1674
1675        // todo: Parse the string for ;s
1676

1677        // Look for the format string in the table of named formats.
1678
for (int i = 0; i < macroTokens.length; i++) {
1679            if (formatString.equals(macroTokens[i].name)) {
1680                if (macroTokens[i].translation == null) {
1681                    // this macro requires special-case code
1682
if (macroTokens[i].name.equals("Currency")) {
1683                        // e.g. "$#,##0.00;($#,##0.00)"
1684
formatString = locale.currencyFormat
1685                            + ";(" +locale.currencyFormat + ")";
1686                    } else {
1687                        throw new Error JavaDoc(
1688                            "Format: internal: token " + macroTokens[i].name +
1689                            " should have translation");
1690                    }
1691                } else {
1692                    formatString = macroTokens[i].translation;
1693                }
1694                break;
1695            }
1696        }
1697
1698        // Add a semi-colon to the end of the string so the end of the string
1699
// looks like the end of an alternate.
1700
if (!formatString.endsWith(";")) {
1701            formatString = formatString + ";";
1702        }
1703
1704        // Scan through the format string for format elements.
1705
List<BasicFormat> formatList = new ArrayList<BasicFormat>();
1706loop:
1707        while (formatString.length() > 0) {
1708            BasicFormat format = null;
1709            String JavaDoc newFormatString = null;
1710            boolean ignoreToken = false;
1711            for (int i = tokens.length - 1; i > 0; i--) {
1712                Token token = tokens[i];
1713                if (formatString.startsWith(token.token)) {
1714                    // Derive the string we will be looking at next time
1715
// around, by chewing the token off the front of the
1716
// string. Special-case code below can change this string,
1717
// if it likes.
1718
String JavaDoc matched = token.token;
1719                    newFormatString = formatString.substring(matched.length());
1720                    if (token.isSpecial()) {
1721                        switch (token.code) {
1722                        case FORMAT_SEMI:
1723                            formatString = newFormatString;
1724                            break loop;
1725
1726                        case FORMAT_POUND:
1727                            switch (numberState) {
1728                            case NOT_IN_A_NUMBER:
1729                                numberState = LEFT_OF_POINT;
1730                                // fall through
1731
case LEFT_OF_POINT:
1732                                digitsLeftOfPoint++;
1733                                break;
1734                            case RIGHT_OF_POINT:
1735                                digitsRightOfPoint++;
1736                                break;
1737                            case RIGHT_OF_EXP:
1738                                digitsRightOfExp++;
1739                                break;
1740                            default:
1741                                throw new Error JavaDoc();
1742                            }
1743                            break;
1744
1745                        case FORMAT_0:
1746                            switch (numberState) {
1747                            case NOT_IN_A_NUMBER:
1748                                numberState = LEFT_OF_POINT;
1749                                // fall through
1750
case LEFT_OF_POINT:
1751                                zeroesLeftOfPoint++;
1752                                break;
1753                            case RIGHT_OF_POINT:
1754                                zeroesRightOfPoint++;
1755                                break;
1756                            case RIGHT_OF_EXP:
1757                                zeroesRightOfExp++;
1758                                break;
1759                            default:
1760                                throw new Error JavaDoc();
1761                            }
1762                            break;
1763
1764                        case FORMAT_M:
1765                        case FORMAT_MM:
1766                        {
1767                            // "m" and "mm" mean minute if immediately after
1768
// "h" or "hh"; month otherwise.
1769
boolean theyMeantMinute = false;
1770                            int j = formatList.size() - 1;
1771                            while (j >= 0) {
1772                                BasicFormat prevFormat = formatList.get(j);
1773                                if (prevFormat instanceof LiteralFormat) {
1774                                    // ignore boilerplate
1775
j--;
1776                                } else if (prevFormat.code == FORMAT_H ||
1777                                           prevFormat.code == FORMAT_HH) {
1778                                    theyMeantMinute = true;
1779                                    break;
1780                                } else {
1781                                    theyMeantMinute = false;
1782                                    break;
1783                                }
1784                            }
1785                            if (theyMeantMinute) {
1786                                format = new DateFormat(
1787                                    (token.code == FORMAT_M
1788                                     ? FORMAT_N
1789                                     : FORMAT_NN),
1790                                    matched,
1791                                    locale,
1792                                    false);
1793                            } else {
1794                                format = token.makeFormat(locale);
1795                            }
1796                            break;
1797                        }
1798
1799                        case FORMAT_DECIMAL:
1800                        {
1801                            numberState = RIGHT_OF_POINT;
1802                            useDecimal = true;
1803                            break;
1804                        }
1805
1806                        case FORMAT_THOUSEP:
1807                        {
1808                            if (numberState == LEFT_OF_POINT) {
1809                                // e.g. "#,##"
1810
useThouSep = true;
1811                            } else {
1812                                // e.g. "ddd, mmm dd, yyy"
1813
format = token.makeFormat(locale);
1814                            }
1815                            break;
1816                        }
1817
1818                        case FORMAT_TIMESEP:
1819                        {
1820                            format = new LiteralFormat(locale.timeSeparator);
1821                            break;
1822                        }
1823
1824                        case FORMAT_DATESEP:
1825                        {
1826                            format = new LiteralFormat(locale.dateSeparator);
1827                            break;
1828                        }
1829
1830                        case FORMAT_BACKSLASH:
1831                        {
1832                            // Display the next character in the format string.
1833
String JavaDoc s = "";
1834                            if (formatString.length() == 1) {
1835                                // Backslash is the last character in the
1836
// string.
1837
s = "";
1838                                newFormatString = "";
1839                            } else {
1840                                s = formatString.substring(1,2);
1841                                newFormatString = formatString.substring(2);
1842                            }
1843                            format = new LiteralFormat(s);
1844                            break;
1845                        }
1846
1847                        case FORMAT_E_MINUS_UPPER:
1848                        case FORMAT_E_PLUS_UPPER:
1849                        case FORMAT_E_MINUS_LOWER:
1850                        case FORMAT_E_PLUS_LOWER:
1851                        {
1852                            numberState = RIGHT_OF_EXP;
1853                            expFormat = token.code;
1854                            if (zeroesLeftOfPoint == 0 &&
1855                                zeroesRightOfPoint == 0) {
1856                                // We need a mantissa, so that format(123.45,
1857
// "E+") gives "1E+2", not "0E+2" or "E+2".
1858
zeroesLeftOfPoint = 1;
1859                            }
1860                            break;
1861                        }
1862
1863                        case FORMAT_QUOTE:
1864                        {
1865                            // Display the string inside the double quotation
1866
// marks.
1867
String JavaDoc s;
1868                            int j = formatString.indexOf("\"", 1);
1869                            if (j == -1) {
1870                                // The string did not contain a closing quote.
1871
// Use the whole string.
1872
s = formatString.substring(1);
1873                                newFormatString = "";
1874                            } else {
1875                                // Take the string inside the quotes.
1876
s = formatString.substring(1, j);
1877                                newFormatString = formatString.substring(
1878                                    j + 1);
1879                            }
1880                            format = new LiteralFormat(s);
1881                            break;
1882                        }
1883
1884                        case FORMAT_UPPER:
1885                        {
1886                            stringCase = CASE_UPPER;
1887                            break;
1888                        }
1889
1890                        case FORMAT_LOWER:
1891                        {
1892                            stringCase = CASE_LOWER;
1893                            break;
1894                        }
1895
1896                        case FORMAT_FILL_FROM_LEFT:
1897                        {
1898                            fillFromRight = false;
1899                            break;
1900                        }
1901
1902                        case FORMAT_GENERAL_NUMBER:
1903                        {
1904                            format = new JavaFormat(locale.locale);
1905                            break;
1906                        }
1907
1908                        case FORMAT_GENERAL_DATE:
1909                        {
1910                            format = new JavaFormat(locale.locale);
1911                            break;
1912                        }
1913
1914                        case FORMAT_INTL_CURRENCY:
1915                        {
1916                            format = new LiteralFormat(locale.currencySymbol);
1917                            break;
1918                        }
1919
1920                        default:
1921                            throw new Error JavaDoc();
1922                        }
1923                        if (format == null) {
1924                            // If the special-case code does not set format,
1925
// we should not create a format element. (The
1926
// token probably caused some flag to be set.)
1927
ignoreToken = true;
1928                            ignored.append(matched);
1929                        } else {
1930                            prevIgnored = ignored.toString();
1931                            ignored.setLength(0);
1932                        }
1933                    } else {
1934                        format = token.makeFormat(locale);
1935                    }
1936                    break;
1937                }
1938            }
1939
1940            if (format == null && !ignoreToken) {
1941                // None of the standard format elements matched. Make the
1942
// current character into a literal.
1943
format = new LiteralFormat(
1944                    formatString.substring(0,1));
1945                newFormatString = formatString.substring(1);
1946            }
1947
1948            if (format != null) {
1949                if (numberState != NOT_IN_A_NUMBER) {
1950                    // Having seen a few number tokens, we're looking at a
1951
// non-number token. Create the number first.
1952
NumericFormat numericFormat = new NumericFormat(
1953                        prevIgnored, locale, expFormat, digitsLeftOfPoint,
1954                        zeroesLeftOfPoint, digitsRightOfPoint,
1955                        zeroesRightOfPoint, digitsRightOfExp, zeroesRightOfExp,
1956                        useDecimal, useThouSep);
1957                    formatList.add(numericFormat);
1958                    numberState = NOT_IN_A_NUMBER;
1959                    haveSeenNumber = true;
1960                }
1961
1962                formatList.add(format);
1963            }
1964
1965            formatString = newFormatString;
1966        }
1967
1968        if (numberState != NOT_IN_A_NUMBER) {
1969            // We're still in a number. Create a number format.
1970
NumericFormat numericFormat = new NumericFormat(
1971                prevIgnored, locale, expFormat, digitsLeftOfPoint,
1972                zeroesLeftOfPoint, digitsRightOfPoint, zeroesRightOfPoint,
1973                digitsRightOfExp, zeroesRightOfExp, useDecimal, useThouSep);
1974            formatList.add(numericFormat);
1975            numberState = NOT_IN_A_NUMBER;
1976            haveSeenNumber = true;
1977        }
1978
1979        // The is the end of an alternate - or of the whole format string.
1980
// Push the current list of formats onto the list of alternates.
1981
BasicFormat[] formats =
1982            formatList.toArray(new BasicFormat[formatList.size()]);
1983
1984        // If they used some symbol like 'AM/PM' in the format string, tell all
1985
// date formats to use twelve hour clock. Likewise, figure out the
1986
// multiplier implied by their use of "%" or ",".
1987
boolean twelveHourClock = false;
1988        int decimalShift = 0;
1989        for (int i = 0; i < formats.length; i++) {
1990            switch (formats[i].code) {
1991            case FORMAT_UPPER_AM_SOLIDUS_PM:
1992            case FORMAT_LOWER_AM_SOLIDUS_PM:
1993            case FORMAT_UPPER_A_SOLIDUS_P:
1994            case FORMAT_LOWER_A_SOLIDUS_P:
1995            case FORMAT_AMPM:
1996                twelveHourClock = true;
1997                break;
1998
1999            case FORMAT_PERCENT:
2000                // If "%" occurs, the number should be multiplied by 100.
2001
decimalShift += 2;
2002                break;
2003
2004            case FORMAT_THOUSEP:
2005                // If there is a thousands separator (",") immediately to the
2006
// left of the point, or at the end of the number, divide the
2007
// number by 1000. (Or by 1000^n if there are more than one.)
2008
if (haveSeenNumber &&
2009                    i + 1 < formats.length &&
2010                    formats[i + 1].code != FORMAT_THOUSEP &&
2011                    formats[i + 1].code != FORMAT_0 &&
2012                    formats[i + 1].code != FORMAT_POUND) {
2013                    for (int j = i;
2014                         j >= 0 && formats[j].code == FORMAT_THOUSEP;
2015                         j--) {
2016                        decimalShift -= 3;
2017                        formats[j] = new LiteralFormat(""); // ignore
2018
}
2019                }
2020                break;
2021
2022            default:
2023            }
2024        }
2025
2026        if (twelveHourClock) {
2027            for (int i = 0; i < formats.length; i++) {
2028                if (formats[i] instanceof DateFormat) {
2029                    ((DateFormat) formats[i]).setTwelveHourClock(true);
2030                }
2031            }
2032        }
2033
2034        if (decimalShift != 0) {
2035            for (int i = 0; i < formats.length; i++) {
2036                if (formats[i] instanceof NumericFormat) {
2037                    ((NumericFormat) formats[i]).decimalShift = decimalShift;
2038                }
2039            }
2040        }
2041
2042        // Create a CompoundFormat containing all of the format elements.
2043
BasicFormat alternateFormat =
2044                formats.length == 0 ? null :
2045                formats.length == 1 ? formats[0] :
2046                new CompoundFormat(formats);
2047        alternateFormatList.add(alternateFormat);
2048        return formatString;
2049    }
2050
2051    public String JavaDoc format(Object JavaDoc o)
2052    {
2053        StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
2054        format(o, buf);
2055        return buf.toString();
2056    }
2057
2058    private StringBuilder JavaDoc format(Object JavaDoc o, StringBuilder JavaDoc buf) {
2059        if (o == null) {
2060            format.formatNull(buf);
2061        } else {
2062            // For final classes, it is more efficient to switch using
2063
// class equality than using 'instanceof'.
2064
Class JavaDoc<? extends Object JavaDoc> clazz = o.getClass();
2065            if (clazz == Double JavaDoc.class) {
2066                format.format(((Double JavaDoc) o).doubleValue(), buf);
2067            } else if (clazz == Float JavaDoc.class) {
2068                format.format(((Float JavaDoc) o).floatValue(), buf);
2069            } else if (clazz == Integer JavaDoc.class) {
2070                format.format(((Integer JavaDoc) o).intValue(), buf);
2071            } else if (clazz == Long JavaDoc.class) {
2072                format.format(((Long JavaDoc) o).longValue(), buf);
2073            } else if (clazz == Short JavaDoc.class) {
2074                format.format(((Short JavaDoc) o).shortValue(), buf);
2075            } else if (clazz == Byte JavaDoc.class) {
2076                format.format(((Byte JavaDoc) o).byteValue(), buf);
2077            } else if (o instanceof BigDecimal JavaDoc) {
2078                format.format(((BigDecimal JavaDoc) o).doubleValue(), buf);
2079            } else if (o instanceof BigInteger JavaDoc) {
2080                format.format(((BigInteger JavaDoc) o).longValue(), buf);
2081            } else if (clazz == String JavaDoc.class) {
2082                format.format((String JavaDoc) o, buf);
2083            } else if (o instanceof java.util.Date JavaDoc) {
2084                // includes java.sql.Date, java.sql.Time and java.sql.Timestamp
2085
format.format((Date) o, buf);
2086            } else {
2087                buf.append(o.toString());
2088            }
2089        }
2090        return buf;
2091    }
2092
2093    public String JavaDoc getFormatString()
2094    {
2095        return formatString;
2096    }
2097
2098    /**
2099     * Locates a {@link Format.FormatLocale} for a given locale.
2100     */

2101    public interface LocaleFormatFactory {
2102        FormatLocale get(Locale locale);
2103    }
2104
2105/**
2106 * Copied from <code>java.lang.FloatingDecimal</code>.
2107 */

2108static class FloatingDecimal {
2109    boolean isExceptional;
2110    boolean isNegative;
2111    int decExponent;
2112    char digits[];
2113    int nDigits;
2114
2115    /*
2116     * Constants of the implementation
2117     * Most are IEEE-754 related.
2118     * (There are more really boring constants at the end.)
2119     */

2120    static final long signMask = 0x8000000000000000L;
2121    static final long expMask = 0x7ff0000000000000L;
2122    static final long fractMask= ~(signMask|expMask);
2123    static final int expShift = 52;
2124    static final int expBias = 1023;
2125    static final long fractHOB = ( 1L<<expShift ); // assumed High-Order bit
2126
static final long expOne = ((long)expBias)<<expShift; // exponent of 1.0
2127
static final int maxSmallBinExp = 62;
2128    static final int minSmallBinExp = -( 63 / 3 );
2129
2130    static final long highbyte = 0xff00000000000000L;
2131    static final long highbit = 0x8000000000000000L;
2132    static final long lowbytes = ~highbyte;
2133
2134    static final int singleSignMask = 0x80000000;
2135    static final int singleExpMask = 0x7f800000;
2136    static final int singleFractMask = ~(singleSignMask|singleExpMask);
2137    static final int singleExpShift = 23;
2138    static final int singleFractHOB = 1<<singleExpShift;
2139    static final int singleExpBias = 127;
2140
2141    /*
2142     * count number of bits from high-order 1 bit to low-order 1 bit,
2143     * inclusive.
2144     */

2145    private static int
2146    countBits( long v ){
2147        //
2148
// the strategy is to shift until we get a non-zero sign bit
2149
// then shift until we have no bits left, counting the difference.
2150
// we do byte shifting as a hack. Hope it helps.
2151
//
2152
if ( v == 0L ) return 0;
2153
2154        while ( ( v & highbyte ) == 0L ){
2155            v <<= 8;
2156        }
2157        while ( v > 0L ) { // i.e. while ((v&highbit) == 0L )
2158
v <<= 1;
2159        }
2160
2161        int n = 0;
2162        while (( v & lowbytes ) != 0L ){
2163            v <<= 8;
2164            n += 8;
2165        }
2166        while ( v != 0L ){
2167            v <<= 1;
2168            n += 1;
2169        }
2170        return n;
2171    }
2172
2173    /*
2174     * Keep big powers of 5 handy for future reference.
2175     */

2176    private static FDBigInt b5p[];
2177
2178    private static FDBigInt
2179    big5pow( int p ){
2180        if ( p < 0 )
2181            throw new RuntimeException JavaDoc( "Assertion botch: negative power of 5");
2182        if ( b5p == null ){
2183            b5p = new FDBigInt[ p+1 ];
2184        }else if (b5p.length <= p ){
2185            FDBigInt t[] = new FDBigInt[ p+1 ];
2186            System.arraycopy( b5p, 0, t, 0, b5p.length );
2187            b5p = t;
2188        }
2189        if ( b5p[p] != null )
2190            return b5p[p];
2191        else if ( p < small5pow.length )
2192            return b5p[p] = new FDBigInt( small5pow[p] );
2193        else if ( p < long5pow.length )
2194            return b5p[p] = new FDBigInt( long5pow[p] );
2195        else {
2196            // construct the damn thing.
2197
// recursively.
2198
int q, r;
2199            // in order to compute 5^p,
2200
// compute its square root, 5^(p/2) and square.
2201
// or, let q = p / 2, r = p -q, then
2202
// 5^p = 5^(q+r) = 5^q * 5^r
2203
q = p >> 1;
2204            r = p - q;
2205            FDBigInt bigq = b5p[q];
2206            if ( bigq == null )
2207                bigq = big5pow ( q );
2208            if ( r < small5pow.length ){
2209                return (b5p[p] = bigq.mult( small5pow[r] ) );
2210            }else{
2211                FDBigInt bigr = b5p[ r ];
2212                if ( bigr == null )
2213                    bigr = big5pow( r );
2214                return (b5p[p] = bigq.mult( bigr ) );
2215            }
2216        }
2217    }
2218
2219    /*
2220     * This is the easy subcase --
2221     * all the significant bits, after scaling, are held in lvalue.
2222     * negSign and decExponent tell us what processing and scaling
2223     * has already been done. Exceptional cases have already been
2224     * stripped out.
2225     * In particular:
2226     * lvalue is a finite number (not Inf, nor NaN)
2227     * lvalue > 0L (not zero, nor negative).
2228     *
2229     * The only reason that we develop the digits here, rather than
2230     * calling on Long.toString() is that we can do it a little faster,
2231     * and besides want to treat trailing 0s specially. If Long.toString
2232     * changes, we should re-evaluate this strategy!
2233     */

2234    private void
2235    developLongDigits( int decExponent, long lvalue, long insignificant ){
2236        char digits[];
2237        int ndigits;
2238        int digitno;
2239        int c;
2240        //
2241
// Discard non-significant low-order bits, while rounding,
2242
// up to insignificant value.
2243
int i;
2244        for ( i = 0; insignificant >= 10L; i++ )
2245            insignificant /= 10L;
2246        if ( i != 0 ){
2247            long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i;
2248
long residue = lvalue % pow10;
2249            lvalue /= pow10;
2250            decExponent += i;
2251            if ( residue >= (pow10>>1) ){
2252                // round up based on the low-order bits we're discarding
2253
lvalue++;
2254            }
2255        }
2256        if ( lvalue <= Integer.MAX_VALUE ){
2257            if ( lvalue <= 0L )
2258                throw new RuntimeException JavaDoc("Assertion botch: value "+lvalue+" <= 0");
2259
2260            // even easier subcase!
2261
// can do int arithmetic rather than long!
2262
int ivalue = (int)lvalue;
2263            digits = new char[ ndigits=10 ];
2264            digitno = ndigits-1;
2265            c = ivalue%10;
2266            ivalue /= 10;
2267            while ( c == 0 ){
2268                decExponent++;
2269                c = ivalue%10;
2270                ivalue /= 10;
2271            }
2272            while ( ivalue != 0){
2273                digits[digitno--] = (char)(c+'0');
2274                decExponent++;
2275                c = ivalue%10;
2276                ivalue /= 10;
2277            }
2278            digits[digitno] = (char)(c+'0');
2279        } else {
2280            // same algorithm as above (same bugs, too )
2281
// but using long arithmetic.
2282
digits = new char[ ndigits=20 ];
2283            digitno = ndigits-1;
2284            c = (int)(lvalue%10L);
2285            lvalue /= 10L;
2286            while ( c == 0 ){
2287                decExponent++;
2288                c = (int)(lvalue%10L);
2289                lvalue /= 10L;
2290            }
2291            while ( lvalue != 0L ){
2292                digits[digitno--] = (char)(c+'0');
2293                decExponent++;
2294                c = (int)(lvalue%10L);
2295                lvalue /= 10;
2296            }
2297            digits[digitno] = (char)(c+'0');
2298        }
2299        char result [];
2300        ndigits -= digitno;
2301        if ( digitno == 0 )
2302            result = digits;
2303        else {
2304            result = new char[ ndigits ];
2305            System.arraycopy( digits, digitno, result, 0, ndigits );
2306        }
2307        this.digits = result;
2308        this.decExponent = decExponent+1;
2309        this.nDigits = ndigits;
2310    }
2311
2312    //
2313
// add one to the least significant digit.
2314
// in the unlikely event there is a carry out,
2315
// deal with it.
2316
// assert that this will only happen where there
2317
// is only one digit, e.g. (float)1e-44 seems to do it.
2318
//
2319
private void
2320    roundup(){
2321        int i;
2322        int q = digits[ i = (nDigits-1)];
2323        if ( q == '9' ){
2324            while ( q == '9' && i > 0 ){
2325                digits[i] = '0';
2326                q = digits[--i];
2327            }
2328            if ( q == '9' ){
2329                // carryout! High-order 1, rest 0s, larger exp.
2330
decExponent += 1;
2331                digits[0] = '1';
2332                return;
2333            }
2334            // else fall through.
2335
}
2336        digits[i] = (char)(q+1);
2337    }
2338
2339    /*
2340     * FIRST IMPORTANT CONSTRUCTOR: DOUBLE
2341     */

2342    public FloatingDecimal( double d )
2343    {
2344        long dBits = Double.doubleToLongBits( d );
2345        long fractBits;
2346        int binExp;
2347        int nSignificantBits;
2348
2349        // discover and delete sign
2350
if ( (dBits&signMask) != 0 ){
2351            isNegative = true;
2352            dBits ^= signMask;
2353        } else {
2354            isNegative = false;
2355        }
2356        // Begin to unpack
2357
// Discover obvious special cases of NaN and Infinity.
2358
binExp = (int)( (dBits&expMask) >> expShift );
2359        fractBits = dBits&fractMask;
2360        if ( binExp == (int)(expMask>>expShift) ) {
2361            isExceptional = true;
2362            if ( fractBits == 0L ){
2363                digits = infinity;
2364            } else {
2365                digits = notANumber;
2366                isNegative = false; // NaN has no sign!
2367
}
2368            nDigits = digits.length;
2369            return;
2370        }
2371        isExceptional = false;
2372        // Finish unpacking
2373
// Normalize denormalized numbers.
2374
// Insert assumed high-order bit for normalized numbers.
2375
// Subtract exponent bias.
2376
if ( binExp == 0 ){
2377            if ( fractBits == 0L ){
2378                // not a denorm, just a 0!
2379
decExponent = 0;
2380                digits = zero;
2381                nDigits = 1;
2382                return;
2383            }
2384            while ( (fractBits&fractHOB) == 0L ){
2385                fractBits <<= 1;
2386                binExp -= 1;
2387            }
2388            nSignificantBits = expShift + binExp; // recall binExp is - shift count.
2389
binExp += 1;
2390        } else {
2391            fractBits |= fractHOB;
2392            nSignificantBits = expShift+1;
2393        }
2394        binExp -= expBias;
2395        // call the routine that actually does all the hard work.
2396
dtoa( binExp, fractBits, nSignificantBits );
2397    }
2398
2399    /*
2400     * SECOND IMPORTANT CONSTRUCTOR: SINGLE
2401     */

2402    public FloatingDecimal( float f )
2403    {
2404        int fBits = Float.floatToIntBits( f );
2405        int fractBits;
2406        int binExp;
2407        int nSignificantBits;
2408
2409        // discover and delete sign
2410
if ( (fBits&singleSignMask) != 0 ){
2411            isNegative = true;
2412            fBits ^= singleSignMask;
2413        } else {
2414            isNegative = false;
2415        }
2416        // Begin to unpack
2417
// Discover obvious special cases of NaN and Infinity.
2418
binExp = ( (fBits&singleExpMask) >> singleExpShift );
2419        fractBits = fBits&singleFractMask;
2420        if ( binExp == (singleExpMask>>singleExpShift) ) {
2421            isExceptional = true;
2422            if ( fractBits == 0L ){
2423                digits = infinity;
2424            } else {
2425                digits = notANumber;
2426                isNegative = false; // NaN has no sign!
2427
}
2428            nDigits = digits.length;
2429            return;
2430        }
2431        isExceptional = false;
2432        // Finish unpacking
2433
// Normalize denormalized numbers.
2434
// Insert assumed high-order bit for normalized numbers.
2435
// Subtract exponent bias.
2436
if ( binExp == 0 ){
2437            if ( fractBits == 0 ){
2438                // not a denorm, just a 0!
2439
decExponent = 0;
2440                digits = zero;
2441                nDigits = 1;
2442                return;
2443            }
2444            while ( (fractBits&singleFractHOB) == 0 ){
2445                fractBits <<= 1;
2446                binExp -= 1;
2447            }
2448            nSignificantBits = singleExpShift + binExp; // recall binExp is - shift count.
2449
binExp += 1;
2450        } else {
2451            fractBits |= singleFractHOB;
2452            nSignificantBits = singleExpShift+1;
2453        }
2454        binExp -= singleExpBias;
2455        // call the routine that actually does all the hard work.
2456
dtoa( binExp, ((long)fractBits)<<(expShift-singleExpShift), nSignificantBits );
2457    }
2458
2459    private void
2460    dtoa( int binExp, long fractBits, int nSignificantBits )
2461    {
2462        int nFractBits; // number of significant bits of fractBits;
2463
int nTinyBits; // number of these to the right of the point.
2464
int decExp;
2465
2466        // Examine number. Determine if it is an easy case,
2467
// which we can do pretty trivially using float/long conversion,
2468
// or whether we must do real work.
2469
nFractBits = countBits( fractBits );
2470        nTinyBits = Math.max( 0, nFractBits - binExp - 1 );
2471        if ( binExp <= maxSmallBinExp && binExp >= minSmallBinExp ){
2472            // Look more closely at the number to decide if,
2473
// with scaling by 10^nTinyBits, the result will fit in
2474
// a long.
2475
if ( (nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64 ) ){
2476                /*
2477                 * We can do this:
2478                 * take the fraction bits, which are normalized.
2479                 * (a) nTinyBits == 0: Shift left or right appropriately
2480                 * to align the binary point at the extreme right, i.e.
2481                 * where a long int point is expected to be. The integer
2482                 * result is easily converted to a string.
2483                 * (b) nTinyBits > 0: Shift right by expShift-nFractBits,
2484                 * which effectively converts to long and scales by
2485                 * 2^nTinyBits. Then multiply by 5^nTinyBits to
2486                 * complete the scaling. We know this won't overflow
2487                 * because we just counted the number of bits necessary
2488                 * in the result. The integer you get from this can
2489                 * then be converted to a string pretty easily.
2490                 */

2491                long halfULP;
2492                if ( nTinyBits == 0 ) {
2493                    if ( binExp > nSignificantBits ){
2494                        halfULP = 1L << ( binExp-nSignificantBits-1);
2495                    } else {
2496                        halfULP = 0L;
2497                    }
2498                    if ( binExp >= expShift ){
2499                        fractBits <<= (binExp-expShift);
2500                    } else {
2501                        fractBits >>>= (expShift-binExp) ;
2502                    }
2503                    developLongDigits( 0, fractBits, halfULP );
2504                    return;
2505                }
2506                /*
2507                 * The following causes excess digits to be printed
2508                 * out in the single-float case. Our manipulation of
2509                 * halfULP here is apparently not correct. If we
2510                 * better understand how this works, perhaps we can
2511                 * use this special case again. But for the time being,
2512                 * we do not.
2513                 * else {
2514                 * fractBits >>>= expShift+1-nFractBits;
2515                 * fractBits *= long5pow[ nTinyBits ];
2516                 * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits);
2517                 * developLongDigits( -nTinyBits, fractBits, halfULP );
2518                 * return;
2519                 * }
2520                 */

2521            }
2522        }
2523        /*
2524         * This is the hard case. We are going to compute large positive
2525         * integers B and S and integer decExp, s.t.
2526         * d = ( B / S ) * 10^decExp
2527         * 1 <= B / S < 10
2528         * Obvious choices are:
2529         * decExp = floor( log10(d) )
2530         * B = d * 2^nTinyBits * 10^max( 0, -decExp )
2531         * S = 10^max( 0, decExp) * 2^nTinyBits
2532         * (noting that nTinyBits has already been forced to non-negative)
2533         * I am also going to compute a large positive integer
2534         * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp )
2535         * i.e. M is (1/2) of the ULP of d, scaled like B.
2536         * When we iterate through dividing B/S and picking off the
2537         * quotient bits, we will know when to stop when the remainder
2538         * is <= M.
2539         *
2540         * We keep track of powers of 2 and powers of 5.
2541         */

2542
2543        /*
2544         * Estimate decimal exponent. (If it is small-ish,
2545         * we could double-check.)
2546         *
2547         * First, scale the mantissa bits such that 1 <= d2 < 2.
2548         * We are then going to estimate
2549         * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
2550         * and so we can estimate
2551         * log10(d) ~=~ log10(d2) + binExp * log10(2)
2552         * take the floor and call it decExp.
2553         * FIXME -- use more precise constants here. It costs no more.
2554         */

2555        double d2 = Double.longBitsToDouble(
2556            expOne | ( fractBits &~ fractHOB ) );
2557        decExp = (int)Math.floor(
2558            (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 );
2559        int B2, B5; // powers of 2 and powers of 5, respectively, in B
2560
int S2, S5; // powers of 2 and powers of 5, respectively, in S
2561
int M2, M5; // powers of 2 and powers of 5, respectively, in M
2562
int Bbits; // binary digits needed to represent B, approx.
2563
int tenSbits; // binary digits needed to represent 10*S, approx.
2564
FDBigInt Sval, Bval, Mval;
2565
2566        B5 = Math.max( 0, -decExp );
2567        B2 = B5 + nTinyBits + binExp;
2568
2569        S5 = Math.max( 0, decExp );
2570        S2 = S5 + nTinyBits;
2571
2572        M5 = B5;
2573        M2 = B2 - nSignificantBits;
2574
2575        /*
2576         * the long integer fractBits contains the (nFractBits) interesting
2577         * bits from the mantissa of d ( hidden 1 added if necessary) followed
2578         * by (expShift+1-nFractBits) zeros. In the interest of compactness,
2579         * I will shift out those zeros before turning fractBits into a
2580         * FDBigInt. The resulting whole number will be
2581         * d * 2^(nFractBits-1-binExp).
2582         */

2583        fractBits >>>= (expShift+1-nFractBits);
2584        B2 -= nFractBits-1;
2585        int common2factor = Math.min( B2, S2 );
2586        B2 -= common2factor;
2587        S2 -= common2factor;
2588        M2 -= common2factor;
2589
2590        /*
2591         * HACK!! For exact powers of two, the next smallest number
2592         * is only half as far away as we think (because the meaning of
2593         * ULP changes at power-of-two bounds) for this reason, we
2594         * hack M2. Hope this works.
2595         */

2596        if ( nFractBits == 1 )
2597            M2 -= 1;
2598
2599        if ( M2 < 0 ){
2600            // oops.
2601
// since we cannot scale M down far enough,
2602
// we must scale the other values up.
2603
B2 -= M2;
2604            S2 -= M2;
2605            M2 = 0;
2606        }
2607        /*
2608         * Construct, Scale, iterate.
2609         * Some day, we'll write a stopping test that takes
2610         * account of the assymetry of the spacing of floating-point
2611         * numbers below perfect powers of 2
2612         * 26 Sept 96 is not that day.
2613         * So we use a symmetric test.
2614         */

2615        char digits[] = this.digits = new char[18];
2616        int ndigit = 0;
2617        boolean low, high;
2618        long lowDigitDifference;
2619        int q;
2620
2621        /*
2622         * Detect the special cases where all the numbers we are about
2623         * to compute will fit in int or long integers.
2624         * In these cases, we will avoid doing FDBigInt arithmetic.
2625         * We use the same algorithms, except that we "normalize"
2626         * our FDBigInts before iterating. This is to make division easier,
2627         * as it makes our fist guess (quotient of high-order words)
2628         * more accurate!
2629         *
2630         * Some day, we'll write a stopping test that takes
2631         * account of the assymetry of the spacing of floating-point
2632         * numbers below perfect powers of 2
2633         * 26 Sept 96 is not that day.
2634         * So we use a symmetric test.
2635         */

2636        Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 ));
2637        tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 ));
2638        if ( Bbits < 64 && tenSbits < 64){
2639            if ( Bbits < 32 && tenSbits < 32){
2640                // wa-hoo! They're all ints!
2641
int b = ((int)fractBits * small5pow[B5] ) << B2;
2642                int s = small5pow[S5] << S2;
2643                int m = small5pow[M5] << M2;
2644                int tens = s * 10;
2645                /*
2646                 * Unroll the first iteration. If our decExp estimate
2647                 * was too high, our first quotient will be zero. In this
2648                 * case, we discard it and decrement decExp.
2649                 */

2650                ndigit = 0;
2651                q = ( b / s );
2652                b = 10 * ( b % s );
2653                m *= 10;
2654                low = (b < m );
2655                high = (b+m > tens );
2656                if ( q >= 10 ){
2657                    // bummer, dude
2658
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2659                } else if ( (q == 0) && ! high ){
2660                    // oops. Usually ignore leading zero.
2661
decExp--;
2662                } else {
2663                    digits[ndigit++] = (char)('0' + q);
2664                }
2665                /*
2666                 * HACK! Java spec sez that we always have at least
2667                 * one digit after the . in either F- or E-form output.
2668                 * Thus we will need more than one digit if we're using
2669                 * E-form
2670                 */

2671                if ( decExp <= -3 || decExp >= 8 ){
2672                    high = low = false;
2673                }
2674                while( ! low && ! high ){
2675                    q = ( b / s );
2676                    b = 10 * ( b % s );
2677                    m *= 10;
2678                    if ( q >= 10 ){
2679                        // bummer, dude
2680
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2681                    }
2682                    if ( m > 0L ){
2683                        low = (b < m );
2684                        high = (b+m > tens );
2685                    } else {
2686                        // hack -- m might overflow!
2687
// in this case, it is certainly > b,
2688
// which won't
2689
// and b+m > tens, too, since that has overflowed
2690
// either!
2691
low = true;
2692                        high = true;
2693                    }
2694                    digits[ndigit++] = (char)('0' + q);
2695                }
2696                lowDigitDifference = (b<<1) - tens;
2697            } else {
2698                // still good! they're all longs!
2699
long b = (fractBits * long5pow[B5] ) << B2;
2700                long s = long5pow[S5] << S2;
2701                long m = long5pow[M5] << M2;
2702                long tens = s * 10L;
2703                /*
2704                 * Unroll the first iteration. If our decExp estimate
2705                 * was too high, our first quotient will be zero. In this
2706                 * case, we discard it and decrement decExp.
2707                 */

2708                ndigit = 0;
2709                q = (int) ( b / s );
2710                b = 10L * ( b % s );
2711                m *= 10L;
2712                low = (b < m );
2713                high = (b+m > tens );
2714                if ( q >= 10 ){
2715                    // bummer, dude
2716
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2717                } else if ( (q == 0) && ! high ){
2718                    // oops. Usually ignore leading zero.
2719
decExp--;
2720                } else {
2721                    digits[ndigit++] = (char)('0' + q);
2722                }
2723                /*
2724                 * HACK! Java spec sez that we always have at least
2725                 * one digit after the . in either F- or E-form output.
2726                 * Thus we will need more than one digit if we're using
2727                 * E-form
2728                 */

2729                if ( decExp <= -3 || decExp >= 8 ){
2730                    high = low = false;
2731                }
2732                while( ! low && ! high ){
2733                    q = (int) ( b / s );
2734                    b = 10 * ( b % s );
2735                    m *= 10;
2736                    if ( q >= 10 ){
2737                        // bummer, dude
2738
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2739                    }
2740                    if ( m > 0L ){
2741                        low = (b < m );
2742                        high = (b+m > tens );
2743                    } else {
2744                        // hack -- m might overflow!
2745
// in this case, it is certainly > b,
2746
// which won't
2747
// and b+m > tens, too, since that has overflowed
2748
// either!
2749
low = true;
2750                        high = true;
2751                    }
2752                    digits[ndigit++] = (char)('0' + q);
2753                }
2754                lowDigitDifference = (b<<1) - tens;
2755            }
2756        } else {
2757            FDBigInt tenSval;
2758            int shiftBias;
2759
2760            /*
2761             * We really must do FDBigInt arithmetic.
2762             * Fist, construct our FDBigInt initial values.
2763             */

2764            Bval = new FDBigInt( fractBits );
2765            if ( B5 != 0 ){
2766                if ( B5 < small5pow.length ){
2767                    Bval = Bval.mult( small5pow[B5] );
2768                } else {
2769                    Bval = Bval.mult( big5pow( B5 ) );
2770                }
2771            }
2772            if ( B2 != 0 ){
2773                Bval.lshiftMe( B2 );
2774            }
2775            Sval = new FDBigInt( big5pow( S5 ) );
2776            if ( S2 != 0 ){
2777                Sval.lshiftMe( S2 );
2778            }
2779            Mval = new FDBigInt( big5pow( M5 ) );
2780            if ( M2 != 0 ){
2781                Mval.lshiftMe( M2 );
2782            }
2783
2784
2785            // normalize so that division works better
2786
Bval.lshiftMe( shiftBias = Sval.normalizeMe() );
2787            Mval.lshiftMe( shiftBias );
2788            tenSval = Sval.mult( 10 );
2789            /*
2790             * Unroll the first iteration. If our decExp estimate
2791             * was too high, our first quotient will be zero. In this
2792             * case, we discard it and decrement decExp.
2793             */

2794            ndigit = 0;
2795            q = Bval.quoRemIteration( Sval );
2796            Mval = Mval.mult( 10 );
2797            low = (Bval.cmp( Mval ) < 0);
2798            high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
2799            if ( q >= 10 ){
2800                // bummer, dude
2801
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2802            } else if ( (q == 0) && ! high ){
2803                // oops. Usually ignore leading zero.
2804
decExp--;
2805            } else {
2806                digits[ndigit++] = (char)('0' + q);
2807            }
2808            /*
2809             * HACK! Java spec sez that we always have at least
2810             * one digit after the . in either F- or E-form output.
2811             * Thus we will need more than one digit if we're using
2812             * E-form
2813             */

2814            if ( decExp <= -3 || decExp >= 8 ){
2815                high = low = false;
2816            }
2817            while( ! low && ! high ){
2818                q = Bval.quoRemIteration( Sval );
2819                Mval = Mval.mult( 10 );
2820                if ( q >= 10 ){
2821                    // bummer, dude
2822
throw new RuntimeException JavaDoc( "Assertion botch: excessivly large digit "+q);
2823                }
2824                low = (Bval.cmp( Mval ) < 0);
2825                high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
2826                digits[ndigit++] = (char)('0' + q);
2827            }
2828            if ( high && low ){
2829                Bval.lshiftMe(1);
2830                lowDigitDifference = Bval.cmp(tenSval);
2831            } else
2832                lowDigitDifference = 0L; // this here only for flow analysis!
2833
}
2834        this.decExponent = decExp+1;
2835        this.digits = digits;
2836        this.nDigits = ndigit;
2837        /*
2838         * Last digit gets rounded based on stopping condition.
2839         */

2840        if ( high ){
2841            if ( low ){
2842                if ( lowDigitDifference == 0L ){
2843                    // it's a tie!
2844
// choose based on which digits we like.
2845
if ( (digits[nDigits-1]&1) != 0 ) roundup();
2846                } else if ( lowDigitDifference > 0 ){
2847                    roundup();
2848                }
2849            } else {
2850                roundup();
2851            }
2852        }
2853    }
2854
2855    public String JavaDoc
2856    toString(){
2857        // most brain-dead version
2858
StringBuilder JavaDoc result = new StringBuilder JavaDoc( nDigits+8 );
2859        if ( isNegative ){ result.append( '-' ); }
2860        if ( isExceptional ){
2861            result.append( digits, 0, nDigits );
2862        } else {
2863            result.append( "0.");
2864            result.append( digits, 0, nDigits );
2865            result.append('e');
2866            result.append( decExponent );
2867        }
2868        return new String JavaDoc(result);
2869    }
2870
2871    public String JavaDoc
2872    toJavaFormatString(){
2873        char result[] = new char[ nDigits + 10 ];
2874        int i = 0;
2875        if ( isNegative ){ result[0] = '-'; i = 1; }
2876        if ( isExceptional ){
2877            System.arraycopy( digits, 0, result, i, nDigits );
2878            i += nDigits;
2879        } else {
2880            if ( decExponent > 0 && decExponent < 8 ){
2881                // print digits.digits.
2882
int charLength = Math.min( nDigits, decExponent );
2883                System.arraycopy( digits, 0, result, i, charLength );
2884                i += charLength;
2885                if ( charLength < decExponent ){
2886                    charLength = decExponent-charLength;
2887                    System.arraycopy( zero, 0, result, i, charLength );
2888                    i += charLength;
2889                    result[i++] = '.';
2890                    result[i++] = '0';
2891                } else {
2892                    result[i++] = '.';
2893                    if ( charLength < nDigits ){
2894                        int t = nDigits - charLength;
2895                        System.arraycopy( digits, charLength, result, i, t );
2896                        i += t;
2897                    } else{
2898                        result[i++] = '0';
2899                    }
2900                }
2901            } else if ( decExponent <=0 && decExponent > -3 ){
2902                result[i++] = '0';
2903                result[i++] = '.';
2904                if ( decExponent != 0 ){
2905                    System.arraycopy( zero, 0, result, i, -decExponent );
2906                    i -= decExponent;
2907                }
2908                System.arraycopy( digits, 0, result, i, nDigits );
2909                i += nDigits;
2910            } else {
2911                result[i++] = digits[0];
2912                result[i++] = '.';
2913                if ( nDigits > 1 ){
2914                    System.arraycopy( digits, 1, result, i, nDigits-1 );
2915                    i += nDigits-1;
2916                } else {
2917                    result[i++] = '0';
2918                }
2919                result[i++] = 'E';
2920                int e;
2921                if ( decExponent <= 0 ){
2922                    result[i++] = '-';
2923                    e = -decExponent+1;
2924                } else {
2925                    e = decExponent-1;
2926                }
2927                // decExponent has 1, 2, or 3, digits
2928
if ( e <= 9 ) {
2929                    result[i++] = (char)( e+'0' );
2930                } else if ( e <= 99 ){
2931                    result[i++] = (char)( e/10 +'0' );
2932                    result[i++] = (char)( e%10 + '0' );
2933                } else {
2934                    result[i++] = (char)(e/100+'0');
2935                    e %= 100;
2936                    result[i++] = (char)(e/10+'0');
2937                    result[i++] = (char)( e%10 + '0' );
2938                }
2939            }
2940        }
2941        return new String JavaDoc(result, 0, i);
2942    }
2943
2944    // jhyde added
2945
public FloatingDecimal(long n)
2946    {
2947        isExceptional = false; // I don't think longs can be exceptional
2948
if (n < 0) {
2949            isNegative = true;
2950            n = -n; // if n == MIN_LONG, oops!
2951
} else {
2952            isNegative = false;
2953        }
2954        if (n == 0) {
2955            nDigits = 1;
2956            digits = new char[] {'0','0','0','0','0','0','0','0'};
2957            decExponent = 0;
2958        } else {
2959            nDigits = 0;
2960            for (long m = n; m != 0; m = m / 10) {
2961                nDigits++;
2962            }
2963            decExponent = nDigits;
2964            digits = new char[nDigits];
2965            int i = nDigits - 1;
2966            for (long m = n; m != 0; m = m / 10) {
2967                digits[i--] = (char) ('0' + (m % 10));
2968            }
2969        }
2970    }
2971
2972    // jhyde added
2973
public void shift(int i)
2974    {
2975        if (isExceptional ||
2976            nDigits == 1 && digits[0] == '0') {
2977            ; // don't multiply zero
2978
} else {
2979            decExponent += i;
2980        }
2981    }
2982
2983    // jhyde added
2984
public String JavaDoc toJavaFormatString(
2985        int minDigitsLeftOfDecimal,
2986        char decimalChar, // '.' or ','
2987
int minDigitsRightOfDecimal,
2988        int maxDigitsRightOfDecimal, // todo: use
2989
char expChar, // 'E' or 'e'
2990
boolean expSign, // whether to print '+' if exp is positive
2991
int minExpDigits, // minimum digits in exponent
2992
char thousandChar) // ',' or '.', or 0
2993
{
2994        // char result[] = new char[nDigits + 10]; // crashes for 1.000.000,00
2995
// the result length does *not* depend from nDigits
2996
// it is : decExponent
2997
// +maxDigitsRightOfDecimal
2998
// +10 (for decimal point and sign or -Infinity)
2999
// +decExponent/3 (for the thousand separators)
3000
int resultLen = 10 + Math.abs(decExponent)*4/3 + maxDigitsRightOfDecimal;
3001        char result[] = new char[resultLen];
3002        int i = toJavaFormatString(
3003            result, 0, minDigitsLeftOfDecimal, decimalChar,
3004            minDigitsRightOfDecimal, maxDigitsRightOfDecimal, expChar, expSign,
3005            minExpDigits, thousandChar);
3006        return new String JavaDoc(result, 0, i);
3007    }
3008
3009    // jhyde added
3010
private synchronized int toJavaFormatString(
3011        char result[],
3012        int i,
3013        int minDigitsLeftOfDecimal,
3014        char decimalChar, // '.' or ','
3015
int minDigitsRightOfDecimal,
3016        int maxDigitsRightOfDecimal, // todo: use
3017
char expChar, // 'E' or 'e'
3018
boolean expSign, // whether to print '+' if exp is positive
3019
int minExpDigits, // minimum digits in exponent
3020
char thousandChar) // ',' or '.' or 0
3021
{
3022        if (isNegative) {
3023            result[i++] = '-';
3024        }
3025        if (isExceptional) {
3026            System.arraycopy(digits, 0, result, i, nDigits);
3027            i += nDigits;
3028        } else if (expChar == 0) {
3029            // Build a new array of digits, padded with 0s at either end. For
3030
// example, here is the array we would build for 1234.56.
3031
//
3032
// | 0 0 1 2 3 . 4 5 6 0 0 |
3033
// | |- nDigits=6 -----------------------| |
3034
// | |- decExponent=3 -| |
3035
// |- minDigitsLeftOfDecimal=5 --| |
3036
// | |- minDigitsRightOfDecimal=5 --|
3037
// |- wholeDigits=5 -------------|- fractionDigits=5 -----------|
3038
// |- totalDigits=10 -------------------------------------------|
3039
// | |- maxDigitsRightOfDecimal=5 --|
3040
int wholeDigits = Math.max(decExponent, minDigitsLeftOfDecimal),
3041                fractionDigits = Math.max(
3042                    nDigits - decExponent, minDigitsRightOfDecimal),
3043                totalDigits = wholeDigits + fractionDigits;
3044            char[] digits2 = new char[totalDigits];
3045            for (int j = 0; j < totalDigits; j++) {
3046                digits2[j] = '0';
3047            }
3048            for (int j = 0; j < nDigits; j++) {
3049                digits2[wholeDigits - decExponent + j] = digits[j];
3050            }
3051
3052            // Now round. Suppose that we want to round 1234.56 to 1 decimal
3053
// place (that is, maxDigitsRightOfDecimal = 1). Then lastDigit
3054
// initially points to '5'. We find out that we need to round only
3055
// when we see that the next digit ('6') is non-zero.
3056
//
3057
// | 0 0 1 2 3 . 4 5 6 0 0 |
3058
// | | ^ | |
3059
// | maxDigitsRightOfDecimal=1 |
3060
int lastDigit = wholeDigits + maxDigitsRightOfDecimal;
3061            if (lastDigit < totalDigits) {
3062                // We need to truncate -- also round if the trailing digits are
3063
// 5000... or greater.
3064
boolean trailingZeroes = true;
3065                int m = totalDigits;
3066                while (true) {
3067                    m--;
3068                    if (m < 0) {
3069                        // The entire number was 9s. Re-allocate, so we can
3070
// prepend a '1'.
3071
wholeDigits++;
3072                        totalDigits++;
3073                        lastDigit++;
3074                        char[] old = digits2;
3075                        digits2 = new char[totalDigits];
3076                        digits2[0] = '1';
3077                        System.arraycopy(old, 0, digits2, 1, old.length);
3078                        break;
3079                    } else if (m == lastDigit) {
3080                        char d = digits2[m];
3081                        digits2[m] = '0';
3082                        if (d < '5' ||
3083                            d == '5' && trailingZeroes) {
3084                            break; // no need to round
3085
}
3086                    } else if (m > lastDigit) {
3087                        if (digits2[m] > '0') {
3088                            trailingZeroes = false;
3089                        }
3090                        digits2[m] = '0';
3091                    } else if (digits2[m] == '9') {
3092                        digits2[m] = '0';
3093                        // do not break - we have to carry
3094
} else {
3095                        digits2[m]++;
3096                        break; // nothing to carry
3097
}
3098                }
3099            }
3100
3101            // Find the first non-zero digit and the last non-zero digit.
3102
int firstNonZero = wholeDigits,
3103                firstTrailingZero = 0;
3104            for (int j = 0; j < totalDigits; j++) {
3105                if (digits2[j] != '0') {
3106                    if (j < firstNonZero) {
3107                        firstNonZero = j;
3108                    }
3109                    firstTrailingZero = j + 1;
3110                }
3111            }
3112
3113            int firstDigitToPrint = firstNonZero;
3114            if (firstDigitToPrint > wholeDigits - minDigitsLeftOfDecimal) {
3115                firstDigitToPrint = wholeDigits - minDigitsLeftOfDecimal;
3116            }
3117            int lastDigitToPrint = firstTrailingZero;
3118            if (lastDigitToPrint > wholeDigits + maxDigitsRightOfDecimal) {
3119                lastDigitToPrint = wholeDigits + maxDigitsRightOfDecimal;
3120            }
3121            if (lastDigitToPrint < wholeDigits + minDigitsRightOfDecimal) {
3122                lastDigitToPrint = wholeDigits + minDigitsRightOfDecimal;
3123            }
3124
3125            // Now print the number.
3126
for (int j = firstDigitToPrint; j < wholeDigits; j++) {
3127                if (thousandChar != '\0' &&
3128                    (wholeDigits - j) % 3 == 0 &&
3129                    j > firstDigitToPrint &&
3130                    j < wholeDigits - 1) {
3131                    result[i++] = thousandChar;
3132                }
3133                result[i++] = digits2[j];
3134            }
3135            for (int j = wholeDigits; j < lastDigitToPrint; j++) {
3136                if (j == wholeDigits) {
3137                    result[i++] = decimalChar;
3138                }
3139                result[i++] = digits2[j];
3140            }
3141        } else {
3142            // Make a recursive call to print the digits left of the 'E'.
3143
int oldExp = decExponent;
3144            decExponent = Math.min(minDigitsLeftOfDecimal, nDigits);
3145            boolean oldIsNegative = isNegative;
3146            isNegative = false;
3147            i = toJavaFormatString(
3148                result, i, minDigitsLeftOfDecimal, decimalChar,
3149                minDigitsRightOfDecimal, maxDigitsRightOfDecimal, (char) 0,
3150                false, minExpDigits, '\0');
3151            decExponent = oldExp;
3152            isNegative = oldIsNegative;
3153
3154            result[i++] = expChar;
3155            int de = decExponent;
3156            if (nDigits == 1 && digits[0] == '0') {
3157                de = 1; // 0's exponent is 0, but that's not convenient here
3158
}
3159            int e;
3160            if ( de <= 0 ){
3161                result[i++] = '-';
3162                e = -de+1;
3163            } else {
3164                if (expSign) {
3165                    result[i++] = '+';
3166                }
3167                e = de-1;
3168            }
3169            // decExponent has 1, 2, or 3, digits
3170
int nExpDigits = e <= 9 ? 1 : e <= 99 ? 2 : 3;
3171            for (int j = nExpDigits; j < minExpDigits; j++) {
3172                result[i++] = '0';
3173            }
3174            if ( e <= 9 ) {
3175                result[i++] = (char)( e+'0' );
3176            } else if ( e <= 99 ){
3177                result[i++] = (char)( e/10 +'0' );
3178                result[i++] = (char)( e%10 + '0' );
3179            } else {
3180                result[i++] = (char)(e/100+'0');
3181                e %= 100;
3182                result[i++] = (char)(e/10+'0');
3183                result[i++] = (char)( e%10 + '0' );
3184            }
3185        }
3186        return i;
3187    }
3188
3189    private static final int small5pow[] = {
3190        1,
3191        5,
3192        5*5,
3193        5*5*5,
3194        5*5*5*5,
3195        5*5*5*5*5,
3196        5*5*5*5*5*5,
3197        5*5*5*5*5*5*5,
3198        5*5*5*5*5*5*5*5,
3199        5*5*5*5*5*5*5*5*5,
3200        5*5*5*5*5*5*5*5*5*5,
3201        5*5*5*5*5*5*5*5*5*5*5,
3202        5*5*5*5*5*5*5*5*5*5*5*5,
3203        5*5*5*5*5*5*5*5*5*5*5*5*5
3204    };
3205
3206    private static final long long5pow[] = {
3207        1L,
3208        5L,
3209        5L*5,
3210        5L*5*5,
3211        5L*5*5*5,
3212        5L*5*5*5*5,
3213        5L*5*5*5*5*5,
3214        5L*5*5*5*5*5*5,
3215        5L*5*5*5*5*5*5*5,
3216        5L*5*5*5*5*5*5*5*5,
3217        5L*5*5*5*5*5*5*5*5*5,
3218        5L*5*5*5*5*5*5*5*5*5*5,
3219        5L*5*5*5*5*5*5*5*5*5*5*5,
3220        5L*5*5*5*5*5*5*5*5*5*5*5*5,
3221        5L*5*5*5*5*5*5*5*5*5*5*5*5*5,
3222        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3223        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3224        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3225        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3226        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3227        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3228        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3229        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3230        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3231        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3232        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3233        5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
3234    };
3235
3236    // approximately ceil( log2( long5pow[i] ) )
3237
private static final int n5bits[] = {
3238        0,
3239        3,
3240        5,
3241        7,
3242        10,
3243        12,
3244        14,
3245        17,
3246        19,
3247        21,
3248        24,
3249        26,
3250        28,
3251        31,
3252        33,
3253        35,
3254        38,
3255        40,
3256        42,
3257        45,
3258        47,
3259        49,
3260        52,
3261        54,
3262        56,
3263        59,
3264        61,
3265    };
3266
3267    private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' };
3268    private static final char notANumber[] = { 'N', 'a', 'N' };
3269    private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' };
3270}
3271
3272/*
3273 * A really, really simple bigint package
3274 * tailored to the needs of floating base conversion.
3275 */

3276static class FDBigInt {
3277    int nWords; // number of words used
3278
int data[]; // value: data[0] is least significant
3279

3280    private static boolean debugging = false;
3281
3282    public static void setDebugging( boolean d ) { debugging = d; }
3283
3284    public FDBigInt( int v ){
3285        nWords = 1;
3286        data = new int[1];
3287        data[0] = v;
3288    }
3289
3290    public FDBigInt( long v ){
3291        data = new int[2];
3292        data[0] = (int)v;
3293        data[1] = (int)(v>>>32);
3294        nWords = (data[1]==0) ? 1 : 2;
3295    }
3296
3297    public FDBigInt( FDBigInt other ){
3298        data = new int[nWords = other.nWords];
3299        System.arraycopy( other.data, 0, data, 0, nWords );
3300    }
3301
3302    private FDBigInt( int [] d, int n ){
3303        data = d;
3304        nWords = n;
3305    }
3306
3307    /*
3308     * Left shift by c bits.
3309     * Shifts this in place.
3310     */

3311    public void
3312    lshiftMe( int c )throws IllegalArgumentException JavaDoc {
3313        if ( c <= 0 ){
3314            if ( c == 0 )
3315                return; // silly.
3316
else
3317                throw new IllegalArgumentException JavaDoc("negative shift count");
3318        }
3319        int wordcount = c>>5;
3320        int bitcount = c & 0x1f;
3321        int anticount = 32-bitcount;
3322        int t[] = data;
3323        int s[] = data;
3324        if ( nWords+wordcount+1 > t.length ){
3325            // reallocate.
3326
t = new int[ nWords+wordcount+1 ];
3327        }
3328        int target = nWords+wordcount;
3329        int src = nWords-1;
3330        if ( bitcount == 0 ){
3331            // special hack, since an anticount of 32 won't go!
3332
System.arraycopy( s, 0, t, wordcount, nWords );
3333            target = wordcount-1;
3334        } else {
3335            t[target--] = s[src]>>>anticount;
3336            while ( src >= 1 ){
3337                t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount);
3338            }
3339            t[target--] = s[src]<<bitcount;
3340        }
3341        while( target >= 0 ){
3342            t[target--] = 0;
3343        }
3344        data = t;
3345        nWords += wordcount + 1;
3346        // may have constructed high-order word of 0.
3347
// if so, trim it
3348
while ( nWords > 1 && data[nWords-1] == 0 )
3349            nWords--;
3350    }
3351
3352    /*
3353     * normalize this number by shifting until
3354     * the MSB of the number is at 0x08000000.
3355     * This is in preparation for quoRemIteration, below.
3356     * The idea is that, to make division easier, we want the
3357     * divisor to be "normalized" -- usually this means shifting
3358     * the MSB into the high words sign bit. But because we know that
3359     * the quotient will be 0 < q < 10, we would like to arrange that
3360     * the dividend not span up into another word of precision.
3361     * (This needs to be explained more clearly!)
3362     */

3363    public int
3364    normalizeMe() throws IllegalArgumentException JavaDoc {
3365        int src;
3366        int wordcount = 0;
3367        int bitcount = 0;
3368        int v = 0;
3369        for ( SRC= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){
3370            wordcount += 1;
3371        }
3372        if ( src < 0 ){
3373            // oops. Value is zero. Cannot normalize it!
3374
throw new IllegalArgumentException JavaDoc("zero value");
3375        }
3376        /*
3377         * In most cases, we assume that wordcount is zero. This only
3378         * makes sense, as we try not to maintain any high-order
3379         * words full of zeros. In fact, if there are zeros, we will
3380         * simply SHORTEN our number at this point. Watch closely...
3381         */

3382        nWords -= wordcount;
3383        /*
3384         * Compute how far left we have to shift v s.t. its highest-
3385         * order bit is in the right place. Then call lshiftMe to
3386         * do the work.
3387         */

3388        if ( (v & 0xf0000000) != 0 ){
3389            // will have to shift up into the next word.
3390
// too bad.
3391
for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- )
3392                v >>>= 1;
3393        } else {
3394            while ( v <= 0x000fffff ){
3395                // hack: byte-at-a-time shifting
3396
v <<= 8;
3397                bitcount += 8;
3398            }
3399            while ( v <= 0x07ffffff ){
3400                v <<= 1;
3401                bitcount += 1;
3402            }
3403        }
3404        if ( bitcount != 0 )
3405            lshiftMe( bitcount );
3406        return bitcount;
3407    }
3408
3409    /*
3410     * Multiply a FDBigInt by an int.
3411     * Result is a new FDBigInt.
3412     */

3413    public FDBigInt
3414    mult( int iv ) {
3415        long v = iv;
3416        int r[];
3417        long p;
3418
3419        // guess adequate size of r.
3420
r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ];
3421        p = 0L;
3422        for( int i=0; i < nWords; i++ ) {
3423            p += v * ((long)data[i]&0xffffffffL);
3424            r[i] = (int)p;
3425            p >>>= 32;
3426        }
3427        if ( p == 0L){
3428            return new FDBigInt( r, nWords );
3429        } else {
3430            r[nWords] = (int)p;
3431            return new FDBigInt( r, nWords+1 );
3432        }
3433    }
3434
3435    /*
3436     * Multiply a FDBigInt by another FDBigInt.
3437     * Result is a new FDBigInt.
3438     */

3439    public FDBigInt
3440    mult( FDBigInt other ){
3441        // crudely guess adequate size for r
3442
int r[] = new int[ nWords + other.nWords ];
3443        int i;
3444        // I think I am promised zeros...
3445

3446        for( i = 0; i < this.nWords; i++ ){
3447            long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION
3448
long p = 0L;
3449            int j;
3450            for( j = 0; j < other.nWords; j++ ){
3451                p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND.
3452
r[i+j] = (int)p;
3453                p >>>= 32;
3454            }
3455            r[i+j] = (int)p;
3456        }
3457        // compute how much of r we actually needed for all that.
3458
for ( i = r.length-1; i> 0; i--)
3459            if ( r[i] != 0 )
3460                break;
3461        return new FDBigInt( r, i+1 );
3462    }
3463
3464    /*
3465     * Add one FDBigInt to another. Return a FDBigInt
3466     */

3467    public FDBigInt
3468    add( FDBigInt other ){
3469        int i;
3470        int a[], b[];
3471        int n, m;
3472        long c = 0L;
3473        // arrange such that a.nWords >= b.nWords;
3474
// n = a.nWords, m = b.nWords
3475
if ( this.nWords >= other.nWords ){
3476            a = this.data;
3477            n = this.nWords;
3478            b = other.data;
3479            m = other.nWords;
3480        } else {
3481            a = other.data;
3482            n = other.nWords;
3483            b = this.data;
3484            m = this.nWords;
3485        }
3486        int r[] = new int[ n ];
3487        for ( i = 0; i < n; i++ ){
3488            c += (long)a[i] & 0xffffffffL;
3489            if ( i < m ){
3490                c += (long)b[i] & 0xffffffffL;
3491            }
3492            r[i] = (int) c;
3493            c >>= 32; // signed shift.
3494
}
3495        if ( c != 0L ){
3496            // oops -- carry out -- need longer result.
3497
int s[] = new int[ r.length+1 ];
3498            System.arraycopy( r, 0, s, 0, r.length );
3499            s[i++] = (int)c;
3500            return new FDBigInt( s, i );
3501        }
3502        return new FDBigInt( r, i );
3503    }
3504
3505    /*
3506     * Subtract one FDBigInt from another. Return a FDBigInt
3507     * Assert that the result is positive.
3508     */

3509    public FDBigInt
3510    sub( FDBigInt other ){
3511        int r[] = new int[ this.nWords ];
3512        int i;
3513        int n = this.nWords;
3514        int m = other.nWords;
3515        int nzeros = 0;
3516        long c = 0L;
3517        for ( i = 0; i < n; i++ ){
3518            c += (long)this.data[i] & 0xffffffffL;
3519            if ( i < m ){
3520                c -= (long)other.data[i] & 0xffffffffL;
3521            }
3522            if ( ( r[i] = (int) c ) == 0 )
3523                nzeros++;
3524            else
3525                nzeros = 0;
3526            c >>= 32; // signed shift.
3527
}
3528        if ( c != 0L )
3529            throw new RuntimeException JavaDoc("Assertion botch: borrow out of subtract");
3530        while ( i < m )
3531            if ( other.data[i++] != 0 )
3532                throw new RuntimeException JavaDoc("Assertion botch: negative result of subtract");
3533        return new FDBigInt( r, n-nzeros );
3534    }
3535
3536    /*
3537     * Compare FDBigInt with another FDBigInt. Return an integer
3538     * >0: this > other
3539     * 0: this == other
3540     * <0: this < other
3541     */

3542    public int
3543    cmp( FDBigInt other ){
3544        int i;
3545        if ( this.nWords > other.nWords ){
3546            // if any of my high-order words is non-zero,
3547
// then the answer is evident
3548
int j = other.nWords-1;
3549            for ( i = this.nWords-1; i > j ; i-- )
3550                if ( this.data[i] != 0 ) return 1;
3551        }else if ( this.nWords < other.nWords ){
3552            // if any of other's high-order words is non-zero,
3553
// then the answer is evident
3554
int j = this.nWords-1;
3555            for ( i = other.nWords-1; i > j ; i-- )
3556                if ( other.data[i] != 0 ) return -1;
3557        } else{
3558            i = this.nWords-1;
3559        }
3560        for ( ; i > 0 ; i-- )
3561            if ( this.data[i] != other.data[i] )
3562                break;
3563        // careful! want unsigned compare!
3564
// use brute force here.
3565
int a = this.data[i];
3566        int b = other.data[i];
3567        if ( a < 0 ){
3568            // a is really big, unsigned
3569
if ( b < 0 ){
3570                return a-b; // both big, negative
3571
} else {
3572                return 1; // b not big, answer is obvious;
3573
}
3574        } else {
3575            // a is not really big
3576
if ( b < 0 ) {
3577                // but b is really big
3578
return -1;
3579            } else {
3580                return a - b;
3581            }
3582        }
3583    }
3584
3585    /*
3586     * Compute
3587     * q = (int)( this / S )
3588     * this = 10 * ( this mod S )
3589     * Return q.
3590     * This is the iteration step of digit development for output.
3591     * We assume that S has been normalized, as above, and that
3592     * "this" has been lshift'ed accordingly.
3593     * Also assume, of course, that the result, q, can be expressed
3594     * as an integer, 0 <= q < 10.
3595     */

3596    public int
3597    quoRemIteration( FDBigInt S )throws IllegalArgumentException JavaDoc {
3598        // ensure that this and S have the same number of
3599
// digits. If S is properly normalized and q < 10 then
3600
// this must be so.
3601
if ( nWords != S.nWords ){
3602            throw new IllegalArgumentException JavaDoc("disparate values");
3603        }
3604        // estimate q the obvious way. We will usually be
3605
// right. If not, then we're only off by a little and
3606
// will re-add.
3607
int n = nWords-1;
3608        long q = ((long)data[n]&0xffffffffL) / (long)S.data[n];
3609        long diff = 0L;
3610        for ( int i = 0; i <= n ; i++ ){
3611            diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL);
3612            data[i] = (int)diff;
3613            diff >>= 32; // N.B. SIGNED shift.
3614
}
3615        if ( diff != 0L ) {
3616            // damn, damn, damn. q is too big.
3617
// add S back in until this turns +. This should
3618
// not be very many times!
3619
long sum = 0L;
3620            while ( sum == 0L ){
3621                sum = 0L;
3622                for ( int i = 0; i <= n; i++ ){
3623                    sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL);
3624                    data[i] = (int) sum;
3625                    sum >>= 32; // Signed or unsigned, answer is 0 or 1
3626
}
3627                /*
3628                 * Originally the following line read
3629                 * "if ( sum !=0 && sum != -1 )"
3630                 * but that would be wrong, because of the
3631                 * treatment of the two values as entirely unsigned,
3632                 * it would be impossible for a carry-out to be interpreted
3633                 * as -1 -- it would have to be a single-bit carry-out, or
3634                 * +1.
3635                 */

3636                if ( sum !=0 && sum != 1 )
3637                    throw new RuntimeException JavaDoc("Assertion botch: "+sum+" carry out of division correction");
3638                q -= 1;
3639            }
3640        }
3641        // finally, we can multiply this by 10.
3642
// it cannot overflow, right, as the high-order word has
3643
// at least 4 high-order zeros!
3644
long p = 0L;
3645        for ( int i = 0; i <= n; i++ ){
3646            p += 10*((long)data[i]&0xffffffffL);
3647            data[i] = (int)p;
3648            p >>= 32; // SIGNED shift.
3649
}
3650        if ( p != 0L )
3651            throw new RuntimeException JavaDoc("Assertion botch: carry out of *10");
3652
3653        return (int)q;
3654    }
3655
3656    public long
3657    longValue(){
3658        // if this can be represented as a long,
3659
// return the value
3660
int i;
3661        for ( i = this.nWords-1; i > 1 ; i-- ){
3662            if ( data[i] != 0 ){
3663                throw new RuntimeException JavaDoc("Assertion botch: value too big");
3664            }
3665        }
3666        switch(i){
3667        case 1:
3668            if ( data[1] < 0 )
3669                throw new RuntimeException JavaDoc("Assertion botch: value too big");
3670            return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL);
3671        case 0:
3672            return ((long)data[0]&0xffffffffL);
3673        default:
3674            throw new RuntimeException JavaDoc("Assertion botch: longValue confused");
3675        }
3676    }
3677
3678    public String JavaDoc
3679    toString() {
3680        StringBuilder JavaDoc r = new StringBuilder JavaDoc(30);
3681        r.append('[');
3682        int i = Math.min( nWords-1, data.length-1) ;
3683        if ( nWords > data.length ){
3684            r.append( "("+data.length+"<"+nWords+"!)" );
3685        }
3686        for( ; i> 0 ; i-- ){
3687            r.append( Integer.toHexString( data[i] ) );
3688            r.append( ' ' );
3689        }
3690        r.append( Integer.toHexString( data[0] ) );
3691        r.append( ']' );
3692        return new String JavaDoc( r );
3693    }
3694}
3695}
3696
3697// End Format.java
3698
Popular Tags