001    /* DateFormatSymbols.java -- Format over a range of numbers
002       Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010     
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.text;
040    
041    import gnu.java.locale.LocaleHelper;
042    
043    import java.text.spi.DateFormatSymbolsProvider;
044    
045    import java.util.ArrayList;
046    import java.util.List;
047    import java.util.Locale;
048    import java.util.MissingResourceException;
049    import java.util.ResourceBundle;
050    import java.util.ServiceLoader;
051    import java.util.TimeZone;
052     
053    import java.util.spi.TimeZoneNameProvider;
054    
055    /**
056     * This class acts as container for locale specific date/time formatting
057     * information such as the days of the week and the months of the year.
058     * @author Per Bothner (bothner@cygnus.com)
059     *
060     * @date October 24, 1998.
061     */
062    /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3.
063     * Status:  Believed complete and correct.
064     */
065    public class DateFormatSymbols implements java.io.Serializable, Cloneable
066    {
067      String[] ampms;
068      String[] eras;
069      private String localPatternChars;
070      String[] months;
071      String[] shortMonths;
072      String[] shortWeekdays;
073      String[] weekdays;
074    
075      /**
076       * The timezone strings supplied by the runtime.
077       */
078      private String[][] runtimeZoneStrings;
079    
080      /**
081       * Custom timezone strings supplied by {@link #setZoneStrings()}.
082       */
083      private String[][] zoneStrings;
084    
085      private static final long serialVersionUID = -5987973545549424702L;
086    
087      // The order of these prefixes must be the same as in DateFormat
088      private static final String[] formatPrefixes =
089      {
090        "full", "long", "medium", "short"
091      };
092    
093      // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL,
094      // and DEFAULT (constants defined in java.text.DateFormat).  While
095      // not part of the official spec, we need a way to get at locale-specific
096      // default formatting patterns.  They are declared package scope so
097      // as to be easily accessible where needed (DateFormat, SimpleDateFormat).
098      transient String[] dateFormats;
099      transient String[] timeFormats;
100    
101      private String[] formatsForKey(ResourceBundle res, String key) 
102      {
103        String[] values = new String [formatPrefixes.length];
104        for (int i = 0; i < formatPrefixes.length; i++)
105          {
106            values[i] = res.getString(formatPrefixes[i]+key);
107          }
108        return values;
109      }
110    
111      /**
112       * This method initializes a new instance of <code>DateFormatSymbols</code>
113       * by loading the date format information for the specified locale.
114       * This constructor only obtains instances using the runtime's resources;
115       * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
116       * call {@link #getInstance(java.util.Locale)} instead.
117       *
118       * @param locale The locale for which date formatting symbols should
119       *               be loaded. 
120       * @throws MissingResourceException if the resources for the specified
121       *                                  locale could not be found or loaded.
122       * @see #getInstance(java.util.Locale)
123       */
124      public DateFormatSymbols (Locale locale)
125        throws MissingResourceException
126      {
127        ResourceBundle res
128          = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale,
129                                     ClassLoader.getSystemClassLoader());
130    
131        ampms = res.getStringArray ("ampms");
132        eras = res.getStringArray ("eras");
133        localPatternChars = res.getString ("localPatternChars");
134        months = res.getStringArray ("months");
135        shortMonths = res.getStringArray ("shortMonths");
136        shortWeekdays = res.getStringArray ("shortWeekdays");
137        weekdays = res.getStringArray ("weekdays");
138        zoneStrings = (String[][]) res.getObject ("zoneStrings");
139    
140        dateFormats = formatsForKey(res, "DateFormat");
141        timeFormats = formatsForKey(res, "TimeFormat");
142      }
143    
144      /**
145       * This method loads the format symbol information for the default
146       * locale. This constructor only obtains instances using the runtime's resources;
147       * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
148       * call {@link #getInstance()} instead.
149       *
150       * @throws MissingResourceException if the resources for the default
151       *                                  locale could not be found or loaded.
152       * @see #getInstance()
153       */
154      public DateFormatSymbols() 
155        throws MissingResourceException
156      {
157        this (Locale.getDefault());
158      }
159    
160      /**
161       * This method returns the list of strings used for displaying AM or PM.
162       * This is a two element <code>String</code> array indexed by
163       * <code>Calendar.AM</code> and <code>Calendar.PM</code>
164       *
165       * @return The list of AM/PM display strings.
166       */
167      public String[] getAmPmStrings()
168      {
169        return ampms;
170      }
171    
172      /**
173        * This method returns the list of strings used for displaying eras
174        * (e.g., "BC" and "AD").  This is a two element <code>String</code>
175        * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
176        *
177        * @return The list of era disply strings.
178        */
179      public String[] getEras()
180      {
181        return eras;
182      }
183    
184      /**
185        * This method returns the pattern character information for this
186        * object.  This is an 18 character string that contains the characters
187        * that are used in creating the date formatting strings in 
188        * <code>SimpleDateFormat</code>.   The following are the character
189        * positions in the string and which format character they correspond
190        * to (the character in parentheses is the default value in the US English
191        * locale):
192        * <p>
193        * <ul>
194        * <li>0 - era (G)</li>
195        * <li>1 - year (y)</li>
196        * <li>2 - month (M)</li>
197        * <li>3 - day of month (d)</li>
198        * <li>4 - hour out of 12, from 1-12 (h)</li>
199        * <li>5 - hour out of 24, from 0-23 (H)</li>
200        * <li>6 - minute (m)</li>
201        * <li>7 - second (s)</li>
202        * <li>8 - millisecond (S)</li>
203        * <li>9 - date of week (E)</li>
204        * <li>10 - date of year (D)</li>
205        * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
206        * <li>12 - week in year (w)</li>
207        * <li>13 - week in month (W)</li>
208        * <li>14 - am/pm (a)</li>
209        * <li>15 - hour out of 24, from 1-24 (k)</li>
210        * <li>16 - hour out of 12, from 0-11 (K)</li>
211        * <li>17 - time zone (z)</li>
212        * </ul>
213        *
214        * @return The format patter characters
215        */
216      public String getLocalPatternChars()
217      {
218        return localPatternChars;
219      }
220    
221      /**
222       * This method returns the list of strings used for displaying month
223       * names (e.g., "January" and "February").  This is a thirteen element
224       * string array indexed by <code>Calendar.JANUARY</code> through
225       * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
226       * elements because some calendars have thriteen months.
227       *
228       * @return The list of month display strings.
229       */
230      public String[] getMonths ()
231      {
232        return months;
233      }
234    
235      /**
236       * This method returns the list of strings used for displaying abbreviated
237       * month names (e.g., "Jan" and "Feb").  This is a thirteen element
238       * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
239       * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
240       * elements because some calendars have thirteen months.
241       *
242       * @return The list of abbreviated month display strings.
243       */
244      public String[] getShortMonths ()
245      {
246        return shortMonths;
247      }
248    
249      /**
250       * This method returns the list of strings used for displaying abbreviated 
251       * weekday names (e.g., "Sun" and "Mon").  This is an eight element
252       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
253       * through <code>Calendar.SATURDAY</code>.  Note that the first element
254       * of this array is ignored.
255       *
256       * @return This list of abbreviated weekday display strings.
257       */
258      public String[] getShortWeekdays ()
259      {
260        return shortWeekdays;
261      }
262    
263      /**
264       * This method returns the list of strings used for displaying weekday
265       * names (e.g., "Sunday" and "Monday").  This is an eight element
266       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
267       * through <code>Calendar.SATURDAY</code>.  Note that the first element
268       * of this array is ignored.
269       *
270       * @return This list of weekday display strings.
271       */
272      public String[] getWeekdays ()
273      {
274        return weekdays;
275      }
276    
277      /**
278       * This method returns this list of localized timezone display strings.
279       * This is a two dimensional <code>String</code> array where each row in
280       * the array contains five values:
281       * <P>
282       * <ul>
283       * <li>0 - The non-localized time zone id string.</li>
284       * <li>1 - The long name of the time zone (standard time).</li>
285       * <li>2 - The short name of the time zone (standard time).</li>
286       * <li>3 - The long name of the time zone (daylight savings time).</li>
287       * <li>4 - the short name of the time zone (daylight savings time).</li>
288       * </ul>
289       *
290       * @return The list of time zone display strings.
291       */
292      public String[] [] getZoneStrings ()
293      {
294        return zoneStrings;
295      }
296    
297      /**
298       * This method sets the list of strings used to display AM/PM values to
299       * the specified list.
300       * This is a two element <code>String</code> array indexed by
301       * <code>Calendar.AM</code> and <code>Calendar.PM</code>
302       *
303       * @param value The new list of AM/PM display strings.
304       */
305      public void setAmPmStrings (String[] value)
306      {
307        ampms = value;
308      }
309    
310      /**
311       * This method sets the list of strings used to display time eras to
312       * to the specified list.
313       * This is a two element <code>String</code>
314       * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
315       *
316       * @param labels The new list of era display strings.
317       */
318      public void setEras (String[] labels)
319      {
320        eras = labels;
321      }
322    
323      /**
324        * This method sets the list of characters used to specific date/time
325        * formatting strings.
326        * This is an 18 character string that contains the characters
327        * that are used in creating the date formatting strings in 
328        * <code>SimpleDateFormat</code>.   The following are the character
329        * positions in the string and which format character they correspond
330        * to (the character in parentheses is the default value in the US English
331        * locale):
332        * <p>
333        * <ul>
334        * <li>0 - era (G)</li>
335        * <li>1 - year (y)</li>
336        * <li>2 - month (M)</li>
337        * <li>3 - day of month (d)</li>
338        * <li>4 - hour out of 12, from 1-12 (h)</li>
339        * <li>5 - hour out of 24, from 0-23 (H)</li>
340        * <li>6 - minute (m)</li>
341        * <li>7 - second (s)</li>
342        * <li>8 - millisecond (S)</li>
343        * <li>9 - date of week (E)</li>
344        * <li>10 - date of year (D)</li>
345        * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
346        * <li>12 - week in year (w)</li>
347        * <li>13 - week in month (W)</li>
348        * <li>14 - am/pm (a)</li>
349        * <li>15 - hour out of 24, from 1-24 (k)</li>
350        * <li>16 - hour out of 12, from 0-11 (K)</li>
351        * <li>17 - time zone (z)</li>
352        * </ul>
353        *
354        * @param chars The new format pattern characters
355        */
356      public void setLocalPatternChars (String chars)
357      {
358        localPatternChars = chars;
359      }
360    
361      /**
362        * This method sets the list of strings used to display month names.
363        * This is a thirteen element
364        * string array indexed by <code>Calendar.JANUARY</code> through
365        * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
366        * elements because some calendars have thriteen months.
367        *
368        * @param labels The list of month display strings.
369        */
370      public void setMonths (String[] labels)
371      {
372        months = labels;
373      }
374    
375      /**
376       * This method sets the list of strings used to display abbreviated month
377       * names.
378       * This is a thirteen element
379       * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
380       * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
381       * elements because some calendars have thirteen months.
382       *
383       * @param labels The new list of abbreviated month display strings.
384       */
385      public void setShortMonths (String[] labels)
386      {
387        shortMonths = labels;
388      }
389    
390      /**
391       * This method sets the list of strings used to display abbreviated
392       * weekday names.
393       * This is an eight element
394       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
395       * through <code>Calendar.SATURDAY</code>.  Note that the first element
396       * of this array is ignored.
397       *
398       * @param labels This list of abbreviated weekday display strings.
399       */
400      public void setShortWeekdays (String[] labels)
401      {
402        shortWeekdays = labels;
403      }
404    
405      /**
406       * This method sets the list of strings used to display weekday names.
407       * This is an eight element
408       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
409       * through <code>Calendar.SATURDAY</code>.  Note that the first element
410       * of this array is ignored.
411       *
412       * @param labels This list of weekday display strings.
413       */
414      public void setWeekdays (String[] labels)
415      {
416        weekdays = labels;
417      }
418    
419      /**
420       * This method sets the list of display strings for time zones.
421       * This is a two dimensional <code>String</code> array where each row in
422       * the array contains five values:
423       * <P>
424       * <ul>
425       * <li>0 - The non-localized time zone id string.</li>
426       * <li>1 - The long name of the time zone (standard time).</li>
427       * <li>2 - The short name of the time zone (standard time).</li>
428       * <li>3 - The long name of the time zone (daylight savings time).</li>
429       * <li>4 - the short name of the time zone (daylight savings time).</li>
430       * </ul>
431       *
432       * @params zones The list of time zone display strings.
433       */
434      public void setZoneStrings (String[][] zones)
435      {
436        zoneStrings = zones;
437      }
438    
439      /* Does a "deep" equality test - recurses into arrays. */
440      private static boolean equals (Object x, Object y)
441      {
442        if (x == y)
443          return true;
444        if (x == null || y == null)
445          return false;
446        if (! (x instanceof Object[]) || ! (y instanceof Object[]))
447          return x.equals(y);
448        Object[] xa = (Object[]) x;
449        Object[] ya = (Object[]) y;
450        if (xa.length != ya.length)
451          return false;
452        for (int i = xa.length;  --i >= 0; )
453          {
454            if (! equals(xa[i], ya[i]))
455              return false;
456          }
457        return true;
458      }
459    
460      private static int hashCode (Object x)
461      {
462        if (x == null)
463          return 0;
464        if (! (x instanceof Object[]))
465          return x.hashCode();
466        Object[] xa = (Object[]) x;
467        int hash = 0;
468        for (int i = 0;  i < xa.length;  i++)
469          hash = 37 * hashCode(xa[i]);
470        return hash;
471      }
472    
473      /**
474       * This method tests a specified object for equality against this object.
475       * This will be true if and only if the specified object:
476       * <p>
477       * <ul>
478       * <li> Is not <code>null</code>.</li>
479       * <li> Is an instance of <code>DateFormatSymbols</code>.</li>
480       * <li> Contains identical formatting symbols to this object.</li>
481       * </ul>
482       * 
483       * @param obj The <code>Object</code> to test for equality against.
484       *
485       * @return <code>true</code> if the specified object is equal to this one,
486       * <code>false</code> otherwise.
487       */
488      public boolean equals (Object obj)
489      {
490        if (! (obj instanceof DateFormatSymbols))
491          return false;
492        DateFormatSymbols other = (DateFormatSymbols) obj;
493        return (equals(ampms, other.ampms)
494                && equals(eras, other.eras)
495                && equals(localPatternChars, other.localPatternChars)
496                && equals(months, other.months)
497                && equals(shortMonths, other.shortMonths)
498                && equals(shortWeekdays, other.shortWeekdays)
499                && equals(weekdays, other.weekdays)
500                && equals(zoneStrings, other.zoneStrings));
501      }
502    
503      /**
504       * Returns a new copy of this object.
505       *
506       * @return A copy of this object
507       */
508      public Object clone ()
509      {
510        try
511          {
512            return super.clone ();
513          } 
514        catch (CloneNotSupportedException e) 
515          {
516            return null;
517          }
518      }
519    
520      /**
521       * This method returns a hash value for this object.
522       *
523       * @return A hash value for this object.
524       */
525      public int hashCode ()
526      {
527        return (hashCode(ampms)
528                ^ hashCode(eras)
529                ^ hashCode(localPatternChars)
530                ^ hashCode(months)
531                ^ hashCode(shortMonths)
532                ^ hashCode(shortWeekdays)
533                ^ hashCode(weekdays)
534                ^ hashCode(zoneStrings));
535      }
536    
537      /**
538       * Returns a {@link DateFormatSymbols} instance for the
539       * default locale obtained from either the runtime itself
540       * or one of the installed
541       * {@link java.text.spi.DateFormatSymbolsProvider} instances.
542       * This is equivalent to calling
543       * <code>getInstance(Locale.getDefault())</code>.
544       * 
545       * @return a {@link DateFormatSymbols} instance for the default
546       *         locale.
547       * @since 1.6
548       */
549      public static final DateFormatSymbols getInstance()
550      {
551        return getInstance(Locale.getDefault());
552      }
553    
554      /**
555       * Returns a {@link DateFormatSymbols} instance for the
556       * specified locale obtained from either the runtime itself
557       * or one of the installed
558       * {@link java.text.spi.DateFormatSymbolsProvider} instances.
559       * 
560       * @param locale the locale for which an instance should be
561       *               returned.
562       * @return a {@link DateFormatSymbols} instance for the specified
563       *         locale.
564       * @throws NullPointerException if <code>locale</code> is
565       *                              <code>null</code>.
566       * @since 1.6
567       */
568      public static final DateFormatSymbols getInstance(Locale locale)
569      {
570        try
571          {
572            DateFormatSymbols syms = new DateFormatSymbols(locale);
573            return syms;
574          }
575        catch (MissingResourceException e)
576          {
577            /* This means runtime support for the locale
578             * is not available, so we check providers. */
579          }
580        for (DateFormatSymbolsProvider p :
581               ServiceLoader.load(DateFormatSymbolsProvider.class))
582          {
583            for (Locale loc : p.getAvailableLocales())
584              {
585                if (loc.equals(locale))
586                  {
587                    DateFormatSymbols syms = p.getInstance(locale);
588                    if (syms != null)
589                      return syms;
590                    break;
591                  }
592              }
593          }
594        return getInstance(LocaleHelper.getFallbackLocale(locale));
595      }
596    
597    }