KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > trove > util > FastDateFormat


1 /* ====================================================================
2  * Trove - Copyright (c) 1997-2001 Walt Disney Internet Group
3  * ====================================================================
4  * The Tea Software License, Version 1.1
5  *
6  * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Walt Disney Internet Group (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@dig.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35  * written permission of the Walt Disney Internet Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.trove.util;
54
55 import java.util.Date JavaDoc;
56 import java.util.Calendar JavaDoc;
57 import java.util.GregorianCalendar JavaDoc;
58 import java.util.Locale JavaDoc;
59 import java.util.TimeZone JavaDoc;
60 import java.util.List JavaDoc;
61 import java.util.ArrayList JavaDoc;
62 import java.util.Map JavaDoc;
63 import java.util.HashMap JavaDoc;
64 import java.text.DateFormatSymbols JavaDoc;
65 import java.text.DateFormat JavaDoc;
66 import java.text.SimpleDateFormat JavaDoc;
67
68 /******************************************************************************
69  * Similar to {@link java.text.SimpleDateFormat}, but faster and thread-safe.
70  * Only formatting is supported, but all patterns are compatible with
71  * SimpleDateFormat.
72  *
73  * @author Brian S O'Neill
74  * @version
75  * <!--$$Revision:--> 4 <!-- $-->, <!--$$JustDate:--> 01/07/03 <!-- $-->
76  */

77 public class FastDateFormat {
78     /** Style pattern */
79     public static final Object JavaDoc
80         FULL = new Integer JavaDoc(SimpleDateFormat.FULL),
81         LONG = new Integer JavaDoc(SimpleDateFormat.LONG),
82         MEDIUM = new Integer JavaDoc(SimpleDateFormat.MEDIUM),
83         SHORT = new Integer JavaDoc(SimpleDateFormat.SHORT);
84
85     private static final double LOG_10 = Math.log(10);
86
87     private static String JavaDoc cDefaultPattern;
88     private static TimeZone JavaDoc cDefaultTimeZone = TimeZone.getDefault();
89
90     private static Map JavaDoc cTimeZoneDisplayCache = new HashMap JavaDoc();
91
92     private static Map JavaDoc cInstanceCache = new HashMap JavaDoc(7);
93     private static Map JavaDoc cDateInstanceCache = new HashMap JavaDoc(7);
94     private static Map JavaDoc cTimeInstanceCache = new HashMap JavaDoc(7);
95     private static Map JavaDoc cDateTimeInstanceCache = new HashMap JavaDoc(7);
96
97     public static FastDateFormat getInstance() {
98         return getInstance(getDefaultPattern(), null, null, null);
99     }
100
101     /**
102      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
103      */

104     public static FastDateFormat getInstance(String JavaDoc pattern)
105         throws IllegalArgumentException JavaDoc
106     {
107         return getInstance(pattern, null, null, null);
108     }
109
110     /**
111      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
112      * @param timeZone optional time zone, overrides time zone of formatted
113      * date
114      */

115     public static FastDateFormat getInstance
116         (String JavaDoc pattern, TimeZone JavaDoc timeZone) throws IllegalArgumentException JavaDoc
117     {
118         return getInstance(pattern, timeZone, null, null);
119     }
120
121     /**
122      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
123      * @param locale optional locale, overrides system locale
124      */

125     public static FastDateFormat getInstance
126         (String JavaDoc pattern, Locale JavaDoc locale) throws IllegalArgumentException JavaDoc
127     {
128         return getInstance(pattern, null, locale, null);
129     }
130
131     /**
132      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
133      * @param symbols optional date format symbols, overrides symbols for
134      * system locale
135      */

136     public static FastDateFormat getInstance
137         (String JavaDoc pattern, DateFormatSymbols JavaDoc symbols)
138         throws IllegalArgumentException JavaDoc
139     {
140         return getInstance(pattern, null, null, symbols);
141     }
142
143     /**
144      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
145      * @param timeZone optional time zone, overrides time zone of formatted
146      * date
147      * @param locale optional locale, overrides system locale
148      */

149     public static FastDateFormat getInstance
150         (String JavaDoc pattern, TimeZone JavaDoc timeZone, Locale JavaDoc locale)
151         throws IllegalArgumentException JavaDoc
152     {
153         return getInstance(pattern, timeZone, locale, null);
154     }
155
156     /**
157      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
158      * @param timeZone optional time zone, overrides time zone of formatted
159      * date
160      * @param locale optional locale, overrides system locale
161      * @param symbols optional date format symbols, overrides symbols for
162      * provided locale
163      */

164     public static synchronized FastDateFormat getInstance
165         (String JavaDoc pattern, TimeZone JavaDoc timeZone, Locale JavaDoc locale,
166          DateFormatSymbols JavaDoc symbols)
167         throws IllegalArgumentException JavaDoc
168     {
169         Object JavaDoc key = pattern;
170
171         if (timeZone != null) {
172             key = new Pair(key, timeZone);
173         }
174         if (locale != null) {
175             key = new Pair(key, locale);
176         }
177         if (symbols != null) {
178             key = new Pair(key, symbols);
179         }
180
181         FastDateFormat format = (FastDateFormat)cInstanceCache.get(key);
182         if (format == null) {
183             if (locale == null) {
184                 locale = Locale.getDefault();
185             }
186             if (symbols == null) {
187                 symbols = new DateFormatSymbols JavaDoc(locale);
188             }
189             format = new FastDateFormat(pattern, timeZone, locale, symbols);
190             cInstanceCache.put(key, format);
191         }
192         return format;
193     }
194
195     /**
196      * @param style date style: FULL, LONG, MEDIUM, or SHORT
197      * @param timeZone optional time zone, overrides time zone of formatted
198      * date
199      * @param locale optional locale, overrides system locale
200      */

201     public static synchronized FastDateFormat getDateInstance
202         (Object JavaDoc style, TimeZone JavaDoc timeZone, Locale JavaDoc locale)
203         throws IllegalArgumentException JavaDoc
204     {
205         Object JavaDoc key = style;
206
207         if (timeZone != null) {
208             key = new Pair(key, timeZone);
209         }
210         if (locale == null) {
211             key = new Pair(key, locale);
212         }
213
214         FastDateFormat format = (FastDateFormat)cDateInstanceCache.get(key);
215
216         if (format == null) {
217             int ds;
218             try {
219                 ds = ((Integer JavaDoc)style).intValue();
220             }
221             catch (ClassCastException JavaDoc e) {
222                 throw new IllegalArgumentException JavaDoc
223                     ("Illegal date style: " + style);
224             }
225
226             if (locale == null) {
227                 locale = Locale.getDefault();
228             }
229
230             try {
231                 String JavaDoc pattern = ((SimpleDateFormat JavaDoc)DateFormat.getDateInstance(ds, locale)).toPattern();
232                 format = getInstance(pattern, timeZone, locale);
233                 cDateInstanceCache.put(key, format);
234             }
235             catch (ClassCastException JavaDoc e) {
236                 throw new IllegalArgumentException JavaDoc
237                     ("No date pattern for locale: " + locale);
238             }
239         }
240
241         return format;
242     }
243
244     /**
245      * @param style time style: FULL, LONG, MEDIUM, or SHORT
246      * @param timeZone optional time zone, overrides time zone of formatted
247      * date
248      * @param locale optional locale, overrides system locale
249      */

250     public static synchronized FastDateFormat getTimeInstance
251         (Object JavaDoc style, TimeZone JavaDoc timeZone, Locale JavaDoc locale)
252         throws IllegalArgumentException JavaDoc
253     {
254         Object JavaDoc key = style;
255
256         if (timeZone != null) {
257             key = new Pair(key, timeZone);
258         }
259         if (locale != null) {
260             key = new Pair(key, locale);
261         }
262
263         FastDateFormat format = (FastDateFormat)cTimeInstanceCache.get(key);
264
265         if (format == null) {
266             int ts;
267             try {
268                 ts = ((Integer JavaDoc)style).intValue();
269             }
270             catch (ClassCastException JavaDoc e) {
271                 throw new IllegalArgumentException JavaDoc
272                     ("Illegal time style: " + style);
273             }
274
275             if (locale == null) {
276                 locale = Locale.getDefault();
277             }
278
279             try {
280                 String JavaDoc pattern = ((SimpleDateFormat JavaDoc)DateFormat.getTimeInstance(ts, locale)).toPattern();
281                 format = getInstance(pattern, timeZone, locale);
282                 cTimeInstanceCache.put(key, format);
283             }
284             catch (ClassCastException JavaDoc e) {
285                 throw new IllegalArgumentException JavaDoc
286                     ("No date pattern for locale: " + locale);
287             }
288         }
289
290         return format;
291     }
292
293     /**
294      * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
295      * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
296      * @param timeZone optional time zone, overrides time zone of formatted
297      * date
298      * @param locale optional locale, overrides system locale
299      */

300     public static synchronized FastDateFormat getDateTimeInstance
301         (Object JavaDoc dateStyle, Object JavaDoc timeStyle, TimeZone JavaDoc timeZone, Locale JavaDoc locale)
302         throws IllegalArgumentException JavaDoc
303     {
304         Object JavaDoc key = new Pair(dateStyle, timeStyle);
305
306         if (timeZone != null) {
307             key = new Pair(key, timeZone);
308         }
309         if (locale != null) {
310             key = new Pair(key, locale);
311         }
312
313         FastDateFormat format =
314             (FastDateFormat)cDateTimeInstanceCache.get(key);
315
316         if (format == null) {
317             int ds;
318             try {
319                 ds = ((Integer JavaDoc)dateStyle).intValue();
320             }
321             catch (ClassCastException JavaDoc e) {
322                 throw new IllegalArgumentException JavaDoc
323                     ("Illegal date style: " + dateStyle);
324             }
325
326             int ts;
327             try {
328                 ts = ((Integer JavaDoc)timeStyle).intValue();
329             }
330             catch (ClassCastException JavaDoc e) {
331                 throw new IllegalArgumentException JavaDoc
332                     ("Illegal time style: " + timeStyle);
333             }
334             
335             if (locale == null) {
336                 locale = Locale.getDefault();
337             }
338             
339             try {
340                 String JavaDoc pattern = ((SimpleDateFormat JavaDoc)DateFormat.getDateTimeInstance(ds, ts, locale)).toPattern();
341                 format = getInstance(pattern, timeZone, locale);
342                 cDateTimeInstanceCache.put(key, format);
343             }
344             catch (ClassCastException JavaDoc e) {
345                 throw new IllegalArgumentException JavaDoc
346                     ("No date time pattern for locale: " + locale);
347             }
348         }
349
350         return format;
351     }
352
353     static synchronized String JavaDoc getTimeZoneDisplay(TimeZone JavaDoc tz,
354                                                   boolean daylight,
355                                                   int style,
356                                                   Locale JavaDoc locale) {
357         Object JavaDoc key = new TimeZoneDisplayKey(tz, daylight, style, locale);
358         String JavaDoc value = (String JavaDoc)cTimeZoneDisplayCache.get(key);
359         if (value == null) {
360             // This is a very slow call, so cache the results.
361
value = tz.getDisplayName(daylight, style, locale);
362             cTimeZoneDisplayCache.put(key, value);
363         }
364         return value;
365     }
366
367     private static synchronized String JavaDoc getDefaultPattern() {
368         if (cDefaultPattern == null) {
369             cDefaultPattern = new SimpleDateFormat JavaDoc().toPattern();
370         }
371         return cDefaultPattern;
372     }
373
374     /**
375      * Returns a list of Rules.
376      */

377     private static List JavaDoc parse(String JavaDoc pattern, TimeZone JavaDoc timeZone, Locale JavaDoc locale,
378                               DateFormatSymbols JavaDoc symbols) {
379         List JavaDoc rules = new ArrayList JavaDoc();
380
381         String JavaDoc[] ERAs = symbols.getEras();
382         String JavaDoc[] months = symbols.getMonths();
383         String JavaDoc[] shortMonths = symbols.getShortMonths();
384         String JavaDoc[] weekdays = symbols.getWeekdays();
385         String JavaDoc[] shortWeekdays = symbols.getShortWeekdays();
386         String JavaDoc[] AmPmStrings = symbols.getAmPmStrings();
387
388         int length = pattern.length();
389         int[] indexRef = new int[1];
390
391         for (int i=0; i<length; i++) {
392             indexRef[0] = i;
393             String JavaDoc token = parseToken(pattern, indexRef);
394             i = indexRef[0];
395
396             int tokenLen = token.length();
397             if (tokenLen == 0) {
398                 break;
399             }
400
401             Rule rule;
402             char c = token.charAt(0);
403
404             switch (c) {
405             case 'G': // era designator (text)
406
rule = new TextField(Calendar.ERA, ERAs);
407                 break;
408             case 'y': // year (number)
409
if (tokenLen >= 4) {
410                     rule = new UnpaddedNumberField(Calendar.YEAR);
411                 }
412                 else {
413                     rule = new TwoDigitYearField();
414                 }
415                 break;
416             case 'M': // month in year (text and number)
417
if (tokenLen >= 4) {
418                     rule = new TextField(Calendar.MONTH, months);
419                 }
420                 else if (tokenLen == 3) {
421                     rule = new TextField(Calendar.MONTH, shortMonths);
422                 }
423                 else if (tokenLen == 2) {
424                     rule = new TwoDigitMonthField();
425                 }
426                 else {
427                     rule = new UnpaddedMonthField();
428                 }
429                 break;
430             case 'd': // day in month (number)
431
rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen);
432                 break;
433             case 'h': // hour in am/pm (number, 1..12)
434
rule = new TwelveHourField
435                     (selectNumberRule(Calendar.HOUR, tokenLen));
436                 break;
437             case 'H': // hour in day (number, 0..23)
438
rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen);
439                 break;
440             case 'm': // minute in hour (number)
441
rule = selectNumberRule(Calendar.MINUTE, tokenLen);
442                 break;
443             case 's': // second in minute (number)
444
rule = selectNumberRule(Calendar.SECOND, tokenLen);
445                 break;
446             case 'S': // millisecond (number)
447
rule = selectNumberRule(Calendar.MILLISECOND, tokenLen);
448                 break;
449             case 'E': // day in week (text)
450
rule = new TextField
451                     (Calendar.DAY_OF_WEEK,
452                      tokenLen < 4 ? shortWeekdays : weekdays);
453                 break;
454             case 'D': // day in year (number)
455
rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen);
456                 break;
457             case 'F': // day of week in month (number)
458
rule = selectNumberRule
459                     (Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen);
460                 break;
461             case 'w': // week in year (number)
462
rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen);
463                 break;
464             case 'W': // week in month (number)
465
rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen);
466                 break;
467             case 'a': // am/pm marker (text)
468
rule = new TextField(Calendar.AM_PM, AmPmStrings);
469                 break;
470             case 'k': // hour in day (1..24)
471
rule = new TwentyFourHourField
472                     (selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen));
473                 break;
474             case 'K': // hour in am/pm (0..11)
475
rule = selectNumberRule(Calendar.HOUR, tokenLen);
476                 break;
477             case 'z': // time zone (text)
478
if (tokenLen >= 4) {
479                     rule = new TimeZoneRule(timeZone, locale, TimeZone.LONG);
480                 }
481                 else {
482                     rule = new TimeZoneRule(timeZone, locale, TimeZone.SHORT);
483                 }
484                 break;
485             case '\'': // literal text
486
String JavaDoc sub = token.substring(1);
487                 if (sub.length() == 1) {
488                     rule = new CharacterLiteral(sub.charAt(0));
489                 }
490                 else {
491                     rule = new StringLiteral(new String JavaDoc(sub));
492                 }
493                 break;
494             default:
495                 throw new IllegalArgumentException JavaDoc
496                     ("Illegal pattern component: " + token);
497             }
498
499             rules.add(rule);
500         }
501
502         return rules;
503     }
504
505     private static String JavaDoc parseToken(String JavaDoc pattern, int[] indexRef) {
506         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
507
508         int i = indexRef[0];
509         int length = pattern.length();
510
511         char c = pattern.charAt(i);
512         if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
513             // Scan a run of the same character, which indicates a time
514
// pattern.
515
buf.append(c);
516
517             while (i + 1 < length) {
518                 char peek = pattern.charAt(i + 1);
519                 if (peek == c) {
520                     buf.append(c);
521                     i++;
522                 }
523                 else {
524                     break;
525                 }
526             }
527         }
528         else {
529             // This will identify token as text.
530
buf.append('\'');
531
532             boolean inLiteral = false;
533
534             for (; i < length; i++) {
535                 c = pattern.charAt(i);
536                 
537                 if (c == '\'') {
538                     if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
539                         // '' is treated as escaped '
540
i++;
541                         buf.append(c);
542                     }
543                     else {
544                         inLiteral = !inLiteral;
545                     }
546                 }
547                 else if (!inLiteral &&
548                          (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
549                     i--;
550                     break;
551                 }
552                 else {
553                     buf.append(c);
554                 }
555             }
556         }
557
558         indexRef[0] = i;
559         return buf.toString();
560     }
561
562     private static NumberRule selectNumberRule(int field, int padding) {
563         switch (padding) {
564         case 1:
565             return new UnpaddedNumberField(field);
566         case 2:
567             return new TwoDigitNumberField(field);
568         default:
569             return new PaddedNumberField(field, padding);
570         }
571     }
572
573     private final String JavaDoc mPattern;
574     private final TimeZone JavaDoc mTimeZone;
575     private final Locale JavaDoc mLocale;
576     private final Rule[] mRules;
577     private final int mMaxLengthEstimate;
578
579     private FastDateFormat() {
580         this(getDefaultPattern(), null, null, null);
581     }
582
583     /**
584      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
585      */

586     private FastDateFormat(String JavaDoc pattern) throws IllegalArgumentException JavaDoc {
587         this(pattern, null, null, null);
588     }
589
590     /**
591      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
592      * @param timeZone optional time zone, overrides time zone of formatted
593      * date
594      */

595     private FastDateFormat(String JavaDoc pattern, TimeZone JavaDoc timeZone)
596         throws IllegalArgumentException JavaDoc
597     {
598         this(pattern, timeZone, null, null);
599     }
600
601     /**
602      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
603      * @param locale optional locale, overrides system locale
604      */

605     private FastDateFormat(String JavaDoc pattern, Locale JavaDoc locale)
606         throws IllegalArgumentException JavaDoc
607     {
608         this(pattern, null, locale, null);
609     }
610
611     /**
612      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
613      * @param symbols optional date format symbols, overrides symbols for
614      * system locale
615      */

616     private FastDateFormat(String JavaDoc pattern, DateFormatSymbols JavaDoc symbols)
617         throws IllegalArgumentException JavaDoc
618     {
619         this(pattern, null, null, symbols);
620     }
621
622     /**
623      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
624      * @param timeZone optional time zone, overrides time zone of formatted
625      * date
626      * @param locale optional locale, overrides system locale
627      */

628     private FastDateFormat(String JavaDoc pattern, TimeZone JavaDoc timeZone, Locale JavaDoc locale)
629         throws IllegalArgumentException JavaDoc
630     {
631         this(pattern, timeZone, locale, null);
632     }
633
634     /**
635      * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
636      * @param timeZone optional time zone, overrides time zone of formatted
637      * date
638      * @param locale optional locale, overrides system locale
639      * @param symbols optional date format symbols, overrides symbols for
640      * provided locale
641      */

642     private FastDateFormat(String JavaDoc pattern, TimeZone JavaDoc timeZone, Locale JavaDoc locale,
643                            DateFormatSymbols JavaDoc symbols)
644         throws IllegalArgumentException JavaDoc
645     {
646         if (locale == null) {
647             locale = Locale.getDefault();
648         }
649
650         mPattern = pattern;
651         mTimeZone = timeZone;
652         mLocale = locale;
653
654         if (symbols == null) {
655             symbols = new DateFormatSymbols JavaDoc(locale);
656         }
657
658         List JavaDoc rulesList = parse(pattern, timeZone, locale, symbols);
659         mRules = (Rule[])rulesList.toArray(new Rule[rulesList.size()]);
660
661         int len = 0;
662         for (int i=mRules.length; --i >= 0; ) {
663             len += mRules[i].estimateLength();
664         }
665
666         mMaxLengthEstimate = len;
667     }
668
669     public String JavaDoc format(Date JavaDoc date) {
670         Calendar JavaDoc c = new GregorianCalendar JavaDoc(cDefaultTimeZone);
671         c.setTime(date);
672         if (mTimeZone != null) {
673             c.setTimeZone(mTimeZone);
674         }
675         return applyRules(c, new StringBuffer JavaDoc(mMaxLengthEstimate)).toString();
676     }
677
678     public String JavaDoc format(Calendar JavaDoc calendar) {
679         return format(calendar, new StringBuffer JavaDoc(mMaxLengthEstimate))
680             .toString();
681     }
682
683     public StringBuffer JavaDoc format(Date JavaDoc date, StringBuffer JavaDoc buf) {
684         Calendar JavaDoc c = new GregorianCalendar JavaDoc(cDefaultTimeZone);
685         c.setTime(date);
686         if (mTimeZone != null) {
687             c.setTimeZone(mTimeZone);
688         }
689         return applyRules(c, buf);
690     }
691
692     public StringBuffer JavaDoc format(Calendar JavaDoc calendar, StringBuffer JavaDoc buf) {
693         if (mTimeZone != null) {
694             calendar = (Calendar JavaDoc)calendar.clone();
695             calendar.setTimeZone(mTimeZone);
696         }
697         return applyRules(calendar, buf);
698     }
699
700     private StringBuffer JavaDoc applyRules(Calendar JavaDoc calendar, StringBuffer JavaDoc buf) {
701         Rule[] rules = mRules;
702         int len = mRules.length;
703         for (int i=0; i<len; i++) {
704             rules[i].appendTo(buf, calendar);
705         }
706         return buf;
707     }
708
709     public String JavaDoc getPattern() {
710         return mPattern;
711     }
712     
713     /**
714      * Returns the time zone used by this formatter, or null if time zone of
715      * formatted dates is used instead.
716      */

717     public TimeZone JavaDoc getTimeZone() {
718         return mTimeZone;
719     }
720
721     public Locale JavaDoc getLocale() {
722         return mLocale;
723     }
724
725     /**
726      * Returns an estimate for the maximum length date that this date
727      * formatter will produce. The actual formatted length will almost always
728      * be less than or equal to this amount.
729      */

730     public int getMaxLengthEstimate() {
731         return mMaxLengthEstimate;
732     }
733
734     private interface Rule {
735         int estimateLength();
736
737         void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar);
738     }
739
740     private interface NumberRule extends Rule {
741         void appendTo(StringBuffer JavaDoc buffer, int value);
742     }
743
744     private static class CharacterLiteral implements Rule {
745         private final char mValue;
746
747         CharacterLiteral(char value) {
748             mValue = value;
749         }
750
751         public int estimateLength() {
752             return 1;
753         }
754
755         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
756             buffer.append(mValue);
757         }
758     }
759
760     private static class StringLiteral implements Rule {
761         private final String JavaDoc mValue;
762
763         StringLiteral(String JavaDoc value) {
764             mValue = value;
765         }
766
767         public int estimateLength() {
768             return mValue.length();
769         }
770
771         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
772             buffer.append(mValue);
773         }
774     }
775
776     private static class TextField implements Rule {
777         private final int mField;
778         private final String JavaDoc[] mValues;
779
780         TextField(int field, String JavaDoc[] values) {
781             mField = field;
782             mValues = values;
783         }
784
785         public int estimateLength() {
786             int max = 0;
787             for (int i=mValues.length; --i >= 0; ) {
788                 int len = mValues[i].length();
789                 if (len > max) {
790                     max = len;
791                 }
792             }
793             return max;
794         }
795
796         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
797             buffer.append(mValues[calendar.get(mField)]);
798         }
799     }
800
801     private static class UnpaddedNumberField implements NumberRule {
802         private final int mField;
803
804         UnpaddedNumberField(int field) {
805             mField = field;
806         }
807
808         public int estimateLength() {
809             return 4;
810         }
811
812         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
813             appendTo(buffer, calendar.get(mField));
814         }
815
816         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
817             if (value < 10) {
818                 buffer.append((char)(value + '0'));
819             }
820             else if (value < 100) {
821                 buffer.append((char)(value / 10 + '0'));
822                 buffer.append((char)(value % 10 + '0'));
823             }
824             else {
825                 buffer.append(Integer.toString(value));
826             }
827         }
828     }
829
830     private static class UnpaddedMonthField implements NumberRule {
831         UnpaddedMonthField() {
832         }
833
834         public int estimateLength() {
835             return 2;
836         }
837
838         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
839             appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
840         }
841
842         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
843             if (value < 10) {
844                 buffer.append((char)(value + '0'));
845             }
846             else {
847                 buffer.append((char)(value / 10 + '0'));
848                 buffer.append((char)(value % 10 + '0'));
849             }
850         }
851     }
852
853     private static class PaddedNumberField implements NumberRule {
854         private final int mField;
855         private final int mSize;
856
857         PaddedNumberField(int field, int size) {
858             if (size < 3) {
859                 // Should use UnpaddedNumberField or TwoDigitNumberField.
860
throw new IllegalArgumentException JavaDoc();
861             }
862             mField = field;
863             mSize = size;
864         }
865
866         public int estimateLength() {
867             return 4;
868         }
869
870         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
871             appendTo(buffer, calendar.get(mField));
872         }
873
874         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
875             if (value < 100) {
876                 for (int i = mSize; --i >= 2; ) {
877                     buffer.append('0');
878                 }
879                 buffer.append((char)(value / 10 + '0'));
880                 buffer.append((char)(value % 10 + '0'));
881             }
882             else {
883                 int digits;
884                 if (value < 1000) {
885                     digits = 3;
886                 }
887                 else {
888                     digits = (int)(Math.log(value) / LOG_10) + 1;
889                 }
890                 for (int i = mSize; --i >= digits; ) {
891                     buffer.append('0');
892                 }
893                 buffer.append(Integer.toString(value));
894             }
895         }
896     }
897     
898     private static class TwoDigitNumberField implements NumberRule {
899         private final int mField;
900         
901         TwoDigitNumberField(int field) {
902             mField = field;
903         }
904
905         public int estimateLength() {
906             return 2;
907         }
908
909         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
910             appendTo(buffer, calendar.get(mField));
911         }
912
913         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
914             if (value < 100) {
915                 buffer.append((char)(value / 10 + '0'));
916                 buffer.append((char)(value % 10 + '0'));
917             }
918             else {
919                 buffer.append(Integer.toString(value));
920             }
921         }
922     }
923
924     private static class TwoDigitYearField implements NumberRule {
925         TwoDigitYearField() {
926         }
927
928         public int estimateLength() {
929             return 2;
930         }
931
932         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
933             appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
934         }
935
936         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
937             buffer.append((char)(value / 10 + '0'));
938             buffer.append((char)(value % 10 + '0'));
939         }
940     }
941
942     private static class TwoDigitMonthField implements NumberRule {
943         TwoDigitMonthField() {
944         }
945
946         public int estimateLength() {
947             return 2;
948         }
949
950         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
951             appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
952         }
953         
954         public final void appendTo(StringBuffer JavaDoc buffer, int value) {
955             buffer.append((char)(value / 10 + '0'));
956             buffer.append((char)(value % 10 + '0'));
957         }
958     }
959
960     private static class TwelveHourField implements NumberRule {
961         private final NumberRule mRule;
962
963         TwelveHourField(NumberRule rule) {
964             mRule = rule;
965         }
966
967         public int estimateLength() {
968             return mRule.estimateLength();
969         }
970
971         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
972             int value = calendar.get(Calendar.HOUR);
973             if (value == 0) {
974                 value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
975             }
976             mRule.appendTo(buffer, value);
977         }
978
979         public void appendTo(StringBuffer JavaDoc buffer, int value) {
980             mRule.appendTo(buffer, value);
981         }
982     }
983
984     private static class TwentyFourHourField implements NumberRule {
985         private final NumberRule mRule;
986
987         TwentyFourHourField(NumberRule rule) {
988             mRule = rule;
989         }
990
991         public int estimateLength() {
992             return mRule.estimateLength();
993         }
994
995         public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
996             int value = calendar.get(Calendar.HOUR_OF_DAY);
997             if (value == 0) {
998                 value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
999             }
1000            mRule.appendTo(buffer, value);
1001        }
1002
1003        public void appendTo(StringBuffer JavaDoc buffer, int value) {
1004            mRule.appendTo(buffer, value);
1005        }
1006    }
1007
1008    private static class TimeZoneRule implements Rule {
1009        private final TimeZone JavaDoc mTimeZone;
1010        private final Locale JavaDoc mLocale;
1011        private final int mStyle;
1012        private final String JavaDoc mStandard;
1013        private final String JavaDoc mDaylight;
1014
1015        TimeZoneRule(TimeZone JavaDoc timeZone, Locale JavaDoc locale, int style) {
1016            mTimeZone = timeZone;
1017            mLocale = locale;
1018            mStyle = style;
1019
1020            if (timeZone != null) {
1021                mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
1022                mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
1023            }
1024            else {
1025                mStandard = null;
1026                mDaylight = null;
1027            }
1028        }
1029
1030        public int estimateLength() {
1031            if (mTimeZone != null) {
1032                return Math.max(mStandard.length(), mDaylight.length());
1033            }
1034            else if (mStyle == TimeZone.SHORT) {
1035                return 4;
1036            }
1037            else {
1038                return 40;
1039            }
1040        }
1041
1042        public void appendTo(StringBuffer JavaDoc buffer, Calendar JavaDoc calendar) {
1043            TimeZone JavaDoc timeZone;
1044            if ((timeZone = mTimeZone) != null) {
1045                if (timeZone.useDaylightTime() &&
1046                    calendar.get(Calendar.DST_OFFSET) != 0) {
1047
1048                    buffer.append(mDaylight);
1049                }
1050                else {
1051                    buffer.append(mStandard);
1052                }
1053            }
1054            else {
1055                timeZone = calendar.getTimeZone();
1056                if (timeZone.useDaylightTime() &&
1057                    calendar.get(Calendar.DST_OFFSET) != 0) {
1058
1059                    buffer.append(getTimeZoneDisplay
1060                                  (timeZone, true, mStyle, mLocale));
1061                }
1062                else {
1063                    buffer.append(getTimeZoneDisplay
1064                                  (timeZone, false, mStyle, mLocale));
1065                }
1066            }
1067        }
1068    }
1069
1070    private static class TimeZoneDisplayKey {
1071        private final TimeZone JavaDoc mTimeZone;
1072        private final int mStyle;
1073        private final Locale JavaDoc mLocale;
1074
1075        TimeZoneDisplayKey(TimeZone JavaDoc timeZone,
1076                           boolean daylight, int style, Locale JavaDoc locale) {
1077            mTimeZone = timeZone;
1078            if (daylight) {
1079                style |= 0x80000000;
1080            }
1081            mStyle = style;
1082            mLocale = locale;
1083        }
1084
1085        public int hashCode() {
1086            return mStyle * 31 + mLocale.hashCode();
1087        }
1088
1089        public boolean equals(Object JavaDoc obj) {
1090            if (this == obj) {
1091                return true;
1092            }
1093            if (obj instanceof TimeZoneDisplayKey) {
1094                TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj;
1095                return
1096                    mTimeZone.equals(other.mTimeZone) &&
1097                    mStyle == other.mStyle &&
1098                    mLocale.equals(other.mLocale);
1099            }
1100            return false;
1101        }
1102    }
1103}
1104
Popular Tags