Source for java.text.DateFormatSymbols

   1: /* DateFormatSymbols.java -- Format over a range of numbers
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.text;
  40: 
  41: import gnu.java.locale.LocaleHelper;
  42: 
  43: import java.io.IOException;
  44: 
  45: import java.text.spi.DateFormatSymbolsProvider;
  46: 
  47: import java.util.ArrayList;
  48: import java.util.HashMap;
  49: import java.util.List;
  50: import java.util.Locale;
  51: import java.util.Map;
  52: import java.util.MissingResourceException;
  53: import java.util.Properties;
  54: import java.util.ResourceBundle;
  55: import java.util.ServiceLoader;
  56: import java.util.TimeZone;
  57: 
  58: import java.util.spi.TimeZoneNameProvider;
  59: 
  60: /**
  61:  * This class acts as container for locale specific date/time formatting
  62:  * information such as the days of the week and the months of the year.
  63:  *
  64:  * @author Per Bothner (bothner@cygnus.com)
  65:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  66:  * @date October 24, 1998.
  67:  */
  68: /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3.
  69:  * Status:  Believed complete and correct.
  70:  */
  71: public class DateFormatSymbols implements java.io.Serializable, Cloneable
  72: {
  73:   String[] ampms;
  74:   String[] eras;
  75:   private String localPatternChars;
  76:   String[] months;
  77:   String[] shortMonths;
  78:   String[] shortWeekdays;
  79:   String[] weekdays;
  80: 
  81:   /**
  82:    * The set of properties for obtaining the metazone data.
  83:    */
  84:   private static transient final Properties properties;
  85: 
  86:   /**
  87:    * Reads in the properties.
  88:    */
  89:   static
  90:   {
  91:     properties = new Properties();
  92:     try
  93:       {
  94:         properties.load(DateFormatSymbols.class.getResourceAsStream("metazones.properties"));
  95:       }
  96:     catch (IOException exception)
  97:       {
  98:         System.out.println("Failed to load weeks resource: " + exception);
  99:       }
 100:   }
 101: 
 102:   /**
 103:    * The timezone strings supplied by the runtime.
 104:    */
 105:   private String[][] runtimeZoneStrings;
 106: 
 107:   /**
 108:    * Custom timezone strings supplied by {@link #setZoneStrings()}.
 109:    */
 110:   private String[][] zoneStrings;
 111: 
 112:   private static final long serialVersionUID = -5987973545549424702L;
 113: 
 114:   // The order of these prefixes must be the same as in DateFormat
 115:   private static final String[] formatPrefixes =
 116:   {
 117:     "full", "long", "medium", "short"
 118:   };
 119: 
 120:   // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL,
 121:   // and DEFAULT (constants defined in java.text.DateFormat).  While
 122:   // not part of the official spec, we need a way to get at locale-specific
 123:   // default formatting patterns.  They are declared package scope so
 124:   // as to be easily accessible where needed (DateFormat, SimpleDateFormat).
 125:   transient String[] dateFormats;
 126:   transient String[] timeFormats;
 127: 
 128:   private static String[] getStringArray(ResourceBundle res, String name)
 129:   {
 130:     return res.getString(name).split("\u00ae");
 131:   }
 132: 
 133:   private String[][] getZoneStrings(ResourceBundle res, Locale locale)
 134:   {
 135:     List<String[]> allZones = new ArrayList<String[]>();
 136:     try
 137:       {
 138:         Map<String,String[]> systemZones = new HashMap<String,String[]>();
 139:         while (true)
 140:           {
 141:             int index = 0;
 142:             String country = locale.getCountry();
 143:             String data = res.getString("zoneStrings");
 144:             String[] zones = data.split("\u00a9");
 145:             for (int a = 0; a < zones.length; ++a)
 146:               {
 147:                 String[] strings = zones[a].split("\u00ae");
 148:                 String type = properties.getProperty(strings[0] + "." + country);
 149:                 if (type == null)
 150:                   type = properties.getProperty(strings[0] + ".DEFAULT");
 151:                 if (type != null)
 152:                   strings[0] = type;
 153:                 if (strings.length < 5)
 154:                   {
 155:                     String[] newStrings = new String[5];
 156:                     System.arraycopy(strings, 0, newStrings, 0, strings.length);
 157:                     for (int b = strings.length; b < newStrings.length; ++b)
 158:                       newStrings[b] = "";
 159:                     strings = newStrings;
 160:                   }
 161:                 String[] existing = systemZones.get(strings[0]);
 162:                 if (existing != null && existing.length > 1)
 163:                   {
 164:                     for (int b = 1; b < existing.length; ++b)
 165:                       if (!existing[b].equals(""))
 166:                         strings[b] = existing[b];
 167:                   }
 168:                 systemZones.put(strings[0], strings);
 169:               }
 170:             if (res.getLocale() == Locale.ROOT)
 171:               break;
 172:             else
 173:               res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 174:                                              LocaleHelper.getFallbackLocale(res.getLocale()),
 175:                                              ClassLoader.getSystemClassLoader());
 176:           }
 177:         /* Final sanity check for missing values */
 178:         for (String[] zstrings : systemZones.values())
 179:           {
 180:             if (zstrings[1].equals("") && zstrings[2].equals(""))
 181:               {
 182:                 for (Map.Entry<Object,Object> entry : properties.entrySet())
 183:                   {
 184:                     String val = (String) entry.getValue();
 185:                     if (val.equals(zstrings[0]))
 186:                       {
 187:                         String key = (String) entry.getKey();
 188:                         String metazone = key.substring(0, key.indexOf("."));
 189:                         String type = properties.getProperty(metazone + "." + locale.getCountry());
 190:                         if (type == null)
 191:                           type = properties.getProperty(metazone + ".DEFAULT");
 192:                         if (type != null)
 193:                           {
 194:                             String[] ostrings = systemZones.get(type);
 195:                             zstrings[1] = ostrings[1];
 196:                             zstrings[2] = ostrings[2];
 197:                           }
 198:                       }
 199:                   }
 200:               }
 201:           }
 202:         allZones.addAll(systemZones.values());
 203:       }
 204:     catch (MissingResourceException e)
 205:       {
 206:         /* This means runtime support for the locale
 207:          * is not available, so we just include providers. */
 208:       }
 209:     for (TimeZoneNameProvider p :
 210:            ServiceLoader.load(TimeZoneNameProvider.class))
 211:       {
 212:         for (Locale loc : p.getAvailableLocales())
 213:           {
 214:             if (loc.equals(locale))
 215:               {
 216:                 for (String id : TimeZone.getAvailableIDs())
 217:                   {
 218:                     String[] z = new String[5];
 219:                     z[0] = id;
 220:                     z[1] = p.getDisplayName(id, false,
 221:                                             TimeZone.LONG,
 222:                                             locale);
 223:                     z[2] = p.getDisplayName(id, false,
 224:                                             TimeZone.SHORT,
 225:                                             locale);
 226:                     z[3] = p.getDisplayName(id, true,
 227:                                             TimeZone.LONG,
 228:                                             locale);
 229:                     z[4] = p.getDisplayName(id, true,
 230:                                             TimeZone.SHORT,
 231:                                             locale);
 232:                     allZones.add(z);
 233:                   }
 234:                 break;
 235:               }
 236:           }
 237:       }
 238:     return allZones.toArray(new String[allZones.size()][]);
 239:   }
 240: 
 241:   private String[] formatsForKey(ResourceBundle res, String key)
 242:   {
 243:     String[] values = new String[formatPrefixes.length];
 244: 
 245:     for (int i = 0; i < formatPrefixes.length; i++)
 246:       values[i] = res.getString(formatPrefixes[i] + key);
 247: 
 248:     return values;
 249:   }
 250: 
 251:   /**
 252:    * This method initializes a new instance of <code>DateFormatSymbols</code>
 253:    * by loading the date format information for the specified locale.
 254:    * This constructor only obtains instances using the runtime's resources;
 255:    * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
 256:    * call {@link #getInstance(java.util.Locale)} instead.
 257:    *
 258:    * @param locale The locale for which date formatting symbols should
 259:    *               be loaded.
 260:    * @throws MissingResourceException if the resources for the specified
 261:    *                                  locale could not be found or loaded.
 262:    * @see #getInstance(java.util.Locale)
 263:    */
 264:   public DateFormatSymbols (Locale locale)
 265:     throws MissingResourceException
 266:   {
 267:     ResourceBundle res
 268:       = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale,
 269:                                  ClassLoader.getSystemClassLoader());
 270: 
 271:     ampms = getStringArray(res, "ampms");
 272:     eras = getStringArray(res, "eras");
 273:     localPatternChars = res.getString("localPatternChars");
 274:     months = getStringArray(res, "months");
 275:     shortMonths = getStringArray(res, "shortMonths");
 276:     shortWeekdays = getStringArray(res, "shortWeekdays");
 277:     weekdays = getStringArray(res, "weekdays");
 278:     dateFormats = formatsForKey(res, "DateFormat");
 279:     timeFormats = formatsForKey(res, "TimeFormat");
 280:     runtimeZoneStrings = getZoneStrings(res, locale);
 281:   }
 282: 
 283:   /**
 284:    * This method loads the format symbol information for the default
 285:    * locale. This constructor only obtains instances using the runtime's resources;
 286:    * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
 287:    * call {@link #getInstance()} instead.
 288:    *
 289:    * @throws MissingResourceException if the resources for the default
 290:    *                                  locale could not be found or loaded.
 291:    * @see #getInstance()
 292:    */
 293:   public DateFormatSymbols()
 294:     throws MissingResourceException
 295:   {
 296:     this (Locale.getDefault());
 297:   }
 298: 
 299:   /**
 300:    * This method returns the list of strings used for displaying AM or PM.
 301:    * This is a two element <code>String</code> array indexed by
 302:    * <code>Calendar.AM</code> and <code>Calendar.PM</code>
 303:    *
 304:    * @return The list of AM/PM display strings.
 305:    */
 306:   public String[] getAmPmStrings()
 307:   {
 308:     return ampms;
 309:   }
 310: 
 311:   /**
 312:     * This method returns the list of strings used for displaying eras
 313:     * (e.g., "BC" and "AD").  This is a two element <code>String</code>
 314:     * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
 315:     *
 316:     * @return The list of era disply strings.
 317:     */
 318:   public String[] getEras()
 319:   {
 320:     return eras;
 321:   }
 322: 
 323:   /**
 324:     * This method returns the pattern character information for this
 325:     * object.  This is an 18 character string that contains the characters
 326:     * that are used in creating the date formatting strings in
 327:     * <code>SimpleDateFormat</code>.   The following are the character
 328:     * positions in the string and which format character they correspond
 329:     * to (the character in parentheses is the default value in the US English
 330:     * locale):
 331:     * <p>
 332:     * <ul>
 333:     * <li>0 - era (G)</li>
 334:     * <li>1 - year (y)</li>
 335:     * <li>2 - month (M)</li>
 336:     * <li>3 - day of month (d)</li>
 337:     * <li>4 - hour out of 12, from 1-12 (h)</li>
 338:     * <li>5 - hour out of 24, from 0-23 (H)</li>
 339:     * <li>6 - minute (m)</li>
 340:     * <li>7 - second (s)</li>
 341:     * <li>8 - millisecond (S)</li>
 342:     * <li>9 - date of week (E)</li>
 343:     * <li>10 - date of year (D)</li>
 344:     * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
 345:     * <li>12 - week in year (w)</li>
 346:     * <li>13 - week in month (W)</li>
 347:     * <li>14 - am/pm (a)</li>
 348:     * <li>15 - hour out of 24, from 1-24 (k)</li>
 349:     * <li>16 - hour out of 12, from 0-11 (K)</li>
 350:     * <li>17 - time zone (z)</li>
 351:     * </ul>
 352:     *
 353:     * @return The format patter characters
 354:     */
 355:   public String getLocalPatternChars()
 356:   {
 357:     return localPatternChars;
 358:   }
 359: 
 360:   /**
 361:    * This method returns the list of strings used for displaying month
 362:    * names (e.g., "January" and "February").  This is a thirteen element
 363:    * string array indexed by <code>Calendar.JANUARY</code> through
 364:    * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
 365:    * elements because some calendars have thriteen months.
 366:    *
 367:    * @return The list of month display strings.
 368:    */
 369:   public String[] getMonths ()
 370:   {
 371:     return months;
 372:   }
 373: 
 374:   /**
 375:    * This method returns the list of strings used for displaying abbreviated
 376:    * month names (e.g., "Jan" and "Feb").  This is a thirteen element
 377:    * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
 378:    * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
 379:    * elements because some calendars have thirteen months.
 380:    *
 381:    * @return The list of abbreviated month display strings.
 382:    */
 383:   public String[] getShortMonths ()
 384:   {
 385:     return shortMonths;
 386:   }
 387: 
 388:   /**
 389:    * This method returns the list of strings used for displaying abbreviated
 390:    * weekday names (e.g., "Sun" and "Mon").  This is an eight element
 391:    * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
 392:    * through <code>Calendar.SATURDAY</code>.  Note that the first element
 393:    * of this array is ignored.
 394:    *
 395:    * @return This list of abbreviated weekday display strings.
 396:    */
 397:   public String[] getShortWeekdays ()
 398:   {
 399:     return shortWeekdays;
 400:   }
 401: 
 402:   /**
 403:    * This method returns the list of strings used for displaying weekday
 404:    * names (e.g., "Sunday" and "Monday").  This is an eight element
 405:    * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
 406:    * through <code>Calendar.SATURDAY</code>.  Note that the first element
 407:    * of this array is ignored.
 408:    *
 409:    * @return This list of weekday display strings.
 410:    */
 411:   public String[] getWeekdays ()
 412:   {
 413:     return weekdays;
 414:   }
 415: 
 416:   /**
 417:    * This method returns this list of localized timezone display strings.
 418:    * This is a two dimensional <code>String</code> array where each row in
 419:    * the array contains five values:
 420:    * <P>
 421:    * <ul>
 422:    * <li>0 - The non-localized time zone id string.</li>
 423:    * <li>1 - The long name of the time zone (standard time).</li>
 424:    * <li>2 - The short name of the time zone (standard time).</li>
 425:    * <li>3 - The long name of the time zone (daylight savings time).</li>
 426:    * <li>4 - the short name of the time zone (daylight savings time).</li>
 427:    * </ul>
 428:    * <p>
 429:    * If {@link #setZoneStrings(String[][])} has been called, then the value
 430:    * passed to this will be returned.  Otherwise the returned array contains
 431:    * zone names provided by the runtime environment and any
 432:    * {@link java.util.spi.TimeZoneProvider} instances.
 433:    * </p>
 434:    *
 435:    * @return The list of time zone display strings.
 436:    * @see #setZoneStrings(String[][])
 437:    */
 438:   public String[][] getZoneStrings()
 439:   {
 440:     if (zoneStrings != null)
 441:       return zoneStrings;
 442:     return runtimeZoneStrings;
 443:   }
 444: 
 445:   /**
 446:    * This method sets the list of strings used to display AM/PM values to
 447:    * the specified list.
 448:    * This is a two element <code>String</code> array indexed by
 449:    * <code>Calendar.AM</code> and <code>Calendar.PM</code>
 450:    *
 451:    * @param value The new list of AM/PM display strings.
 452:    */
 453:   public void setAmPmStrings (String[] value)
 454:   {
 455:     if(value==null)
 456:       throw new NullPointerException();
 457:     ampms = value;
 458:   }
 459: 
 460:   /**
 461:    * This method sets the list of strings used to display time eras to
 462:    * to the specified list.
 463:    * This is a two element <code>String</code>
 464:    * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
 465:    *
 466:    * @param labels The new list of era display strings.
 467:    */
 468:   public void setEras (String[] labels)
 469:   {
 470:     if(labels==null)
 471:       throw new NullPointerException();
 472:     eras = labels;
 473:   }
 474: 
 475:   /**
 476:     * This method sets the list of characters used to specific date/time
 477:     * formatting strings.
 478:     * This is an 18 character string that contains the characters
 479:     * that are used in creating the date formatting strings in
 480:     * <code>SimpleDateFormat</code>.   The following are the character
 481:     * positions in the string and which format character they correspond
 482:     * to (the character in parentheses is the default value in the US English
 483:     * locale):
 484:     * <p>
 485:     * <ul>
 486:     * <li>0 - era (G)</li>
 487:     * <li>1 - year (y)</li>
 488:     * <li>2 - month (M)</li>
 489:     * <li>3 - day of month (d)</li>
 490:     * <li>4 - hour out of 12, from 1-12 (h)</li>
 491:     * <li>5 - hour out of 24, from 0-23 (H)</li>
 492:     * <li>6 - minute (m)</li>
 493:     * <li>7 - second (s)</li>
 494:     * <li>8 - millisecond (S)</li>
 495:     * <li>9 - date of week (E)</li>
 496:     * <li>10 - date of year (D)</li>
 497:     * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
 498:     * <li>12 - week in year (w)</li>
 499:     * <li>13 - week in month (W)</li>
 500:     * <li>14 - am/pm (a)</li>
 501:     * <li>15 - hour out of 24, from 1-24 (k)</li>
 502:     * <li>16 - hour out of 12, from 0-11 (K)</li>
 503:     * <li>17 - time zone (z)</li>
 504:     * </ul>
 505:     *
 506:     * @param chars The new format pattern characters
 507:     */
 508:   public void setLocalPatternChars (String chars)
 509:   {
 510:     if(chars==null)
 511:       throw new NullPointerException();
 512:     localPatternChars = chars;
 513:   }
 514: 
 515:   /**
 516:     * This method sets the list of strings used to display month names.
 517:     * This is a thirteen element
 518:     * string array indexed by <code>Calendar.JANUARY</code> through
 519:     * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
 520:     * elements because some calendars have thriteen months.
 521:     *
 522:     * @param labels The list of month display strings.
 523:     */
 524:   public void setMonths (String[] labels)
 525:   {
 526:     if(labels==null)
 527:       throw new NullPointerException();
 528:     months = labels;
 529:   }
 530: 
 531:   /**
 532:    * This method sets the list of strings used to display abbreviated month
 533:    * names.
 534:    * This is a thirteen element
 535:    * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
 536:    * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
 537:    * elements because some calendars have thirteen months.
 538:    *
 539:    * @param labels The new list of abbreviated month display strings.
 540:    */
 541:   public void setShortMonths (String[] labels)
 542:   {
 543:     if(labels==null)
 544:       throw new NullPointerException();
 545:     shortMonths = labels;
 546:   }
 547: 
 548:   /**
 549:    * This method sets the list of strings used to display abbreviated
 550:    * weekday names.
 551:    * This is an eight element
 552:    * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
 553:    * through <code>Calendar.SATURDAY</code>.  Note that the first element
 554:    * of this array is ignored.
 555:    *
 556:    * @param labels This list of abbreviated weekday display strings.
 557:    */
 558:   public void setShortWeekdays (String[] labels)
 559:   {
 560:     if(labels==null)
 561:       throw new NullPointerException();
 562:     shortWeekdays = labels;
 563:   }
 564: 
 565:   /**
 566:    * This method sets the list of strings used to display weekday names.
 567:    * This is an eight element
 568:    * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
 569:    * through <code>Calendar.SATURDAY</code>.  Note that the first element
 570:    * of this array is ignored.
 571:    *
 572:    * @param labels This list of weekday display strings.
 573:    */
 574:   public void setWeekdays (String[] labels)
 575:   {
 576:     if(labels==null)
 577:       throw new NullPointerException();
 578:     weekdays = labels;
 579:   }
 580: 
 581:   /**
 582:    * This method sets the list of display strings for time zones.
 583:    * This is a two dimensional <code>String</code> array where each row in
 584:    * the array contains five values:
 585:    * <P>
 586:    * <ul>
 587:    * <li>0 - The non-localized time zone id string.</li>
 588:    * <li>1 - The long name of the time zone (standard time).</li>
 589:    * <li>2 - The short name of the time zone (standard time).</li>
 590:    * <li>3 - The long name of the time zone (daylight savings time).</li>
 591:    * <li>4 - the short name of the time zone (daylight savings time).</li>
 592:    * </ul>
 593:    *
 594:    * @params zones The list of time zone display strings.
 595:    */
 596:   public void setZoneStrings (String[][] zones)
 597:   {
 598:     if(zones==null)
 599:       throw new NullPointerException();
 600:     zoneStrings = zones;
 601:   }
 602: 
 603:   /* Does a "deep" equality test - recurses into arrays. */
 604:   private static boolean equals (Object x, Object y)
 605:   {
 606:     if (x == y)
 607:       return true;
 608:     if (x == null || y == null)
 609:       return false;
 610:     if (! (x instanceof Object[]) || ! (y instanceof Object[]))
 611:       return x.equals(y);
 612:     Object[] xa = (Object[]) x;
 613:     Object[] ya = (Object[]) y;
 614:     if (xa.length != ya.length)
 615:       return false;
 616:     for (int i = xa.length;  --i >= 0; )
 617:       {
 618:         if (! equals(xa[i], ya[i]))
 619:           return false;
 620:       }
 621:     return true;
 622:   }
 623: 
 624:   private static int hashCode (Object x)
 625:   {
 626:     if (x == null)
 627:       return 0;
 628:     if (! (x instanceof Object[]))
 629:       return x.hashCode();
 630:     Object[] xa = (Object[]) x;
 631:     int hash = 0;
 632:     for (int i = 0;  i < xa.length;  i++)
 633:       hash = 37 * hashCode(xa[i]);
 634:     return hash;
 635:   }
 636: 
 637:   /**
 638:    * This method tests a specified object for equality against this object.
 639:    * This will be true if and only if the specified object:
 640:    * <p>
 641:    * <ul>
 642:    * <li> Is not <code>null</code>.</li>
 643:    * <li> Is an instance of <code>DateFormatSymbols</code>.</li>
 644:    * <li> Contains identical formatting symbols to this object.</li>
 645:    * </ul>
 646:    *
 647:    * @param obj The <code>Object</code> to test for equality against.
 648:    *
 649:    * @return <code>true</code> if the specified object is equal to this one,
 650:    * <code>false</code> otherwise.
 651:    */
 652:   public boolean equals (Object obj)
 653:   {
 654:     if (! (obj instanceof DateFormatSymbols))
 655:       return false;
 656:     DateFormatSymbols other = (DateFormatSymbols) obj;
 657:     return (equals(ampms, other.ampms)
 658:             && equals(eras, other.eras)
 659:             && equals(localPatternChars, other.localPatternChars)
 660:             && equals(months, other.months)
 661:             && equals(shortMonths, other.shortMonths)
 662:             && equals(shortWeekdays, other.shortWeekdays)
 663:             && equals(weekdays, other.weekdays)
 664:             && equals(zoneStrings, other.zoneStrings));
 665:   }
 666: 
 667:   /**
 668:    * Returns a new copy of this object.
 669:    *
 670:    * @return A copy of this object
 671:    */
 672:   public Object clone ()
 673:   {
 674:     try
 675:       {
 676:         return super.clone ();
 677:       }
 678:     catch (CloneNotSupportedException e)
 679:       {
 680:         return null;
 681:       }
 682:   }
 683: 
 684:   /**
 685:    * This method returns a hash value for this object.
 686:    *
 687:    * @return A hash value for this object.
 688:    */
 689:   public int hashCode ()
 690:   {
 691:     return (hashCode(ampms)
 692:             ^ hashCode(eras)
 693:             ^ hashCode(localPatternChars)
 694:             ^ hashCode(months)
 695:             ^ hashCode(shortMonths)
 696:             ^ hashCode(shortWeekdays)
 697:             ^ hashCode(weekdays)
 698:             ^ hashCode(zoneStrings));
 699:   }
 700: 
 701:   /**
 702:    * Returns a {@link DateFormatSymbols} instance for the
 703:    * default locale obtained from either the runtime itself
 704:    * or one of the installed
 705:    * {@link java.text.spi.DateFormatSymbolsProvider} instances.
 706:    * This is equivalent to calling
 707:    * <code>getInstance(Locale.getDefault())</code>.
 708:    *
 709:    * @return a {@link DateFormatSymbols} instance for the default
 710:    *         locale.
 711:    * @since 1.6
 712:    */
 713:   public static final DateFormatSymbols getInstance()
 714:   {
 715:     return getInstance(Locale.getDefault());
 716:   }
 717: 
 718:   /**
 719:    * Returns a {@link DateFormatSymbols} instance for the
 720:    * specified locale obtained from either the runtime itself
 721:    * or one of the installed
 722:    * {@link java.text.spi.DateFormatSymbolsProvider} instances.
 723:    *
 724:    * @param locale the locale for which an instance should be
 725:    *               returned.
 726:    * @return a {@link DateFormatSymbols} instance for the specified
 727:    *         locale.
 728:    * @throws NullPointerException if <code>locale</code> is
 729:    *                              <code>null</code>.
 730:    * @since 1.6
 731:    */
 732:   public static final DateFormatSymbols getInstance(Locale locale)
 733:   {
 734:     try
 735:       {
 736:         DateFormatSymbols syms = new DateFormatSymbols(locale);
 737:         return syms;
 738:       }
 739:     catch (MissingResourceException e)
 740:       {
 741:         /* This means runtime support for the locale
 742:          * is not available, so we check providers. */
 743:       }
 744:     for (DateFormatSymbolsProvider p :
 745:            ServiceLoader.load(DateFormatSymbolsProvider.class))
 746:       {
 747:         for (Locale loc : p.getAvailableLocales())
 748:           {
 749:             if (loc.equals(locale))
 750:               {
 751:                 DateFormatSymbols syms = p.getInstance(locale);
 752:                 if (syms != null)
 753:                   return syms;
 754:                 break;
 755:               }
 756:           }
 757:       }
 758:     return getInstance(LocaleHelper.getFallbackLocale(locale));
 759:   }
 760: 
 761: }