Frames | No Frames |
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.Arrays; 49: import java.util.HashMap; 50: import java.util.List; 51: import java.util.Locale; 52: import java.util.Map; 53: import java.util.MissingResourceException; 54: import java.util.Properties; 55: import java.util.ResourceBundle; 56: import java.util.ServiceLoader; 57: import java.util.TimeZone; 58: 59: import java.util.concurrent.ConcurrentMap; 60: import java.util.concurrent.ConcurrentHashMap; 61: 62: import java.util.regex.Pattern; 63: 64: import java.util.spi.TimeZoneNameProvider; 65: 66: /** 67: * This class acts as container for locale specific date/time formatting 68: * information such as the days of the week and the months of the year. 69: * 70: * @author Per Bothner (bothner@cygnus.com) 71: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 72: * @date October 24, 1998. 73: */ 74: /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3. 75: * Status: Believed complete and correct. 76: */ 77: public class DateFormatSymbols implements java.io.Serializable, Cloneable 78: { 79: /** 80: * The set of properties for obtaining the metazone data. 81: */ 82: private static transient final Properties properties; 83: 84: /** 85: * Reads in the properties. 86: */ 87: static 88: { 89: properties = new Properties(); 90: try 91: { 92: properties.load(DateFormatSymbols.class.getResourceAsStream("metazones.properties")); 93: } 94: catch (IOException exception) 95: { 96: System.out.println("Failed to load weeks resource: " + exception); 97: } 98: } 99: 100: private static final Pattern ZONE_SEP = Pattern.compile("\u00a9"); 101: 102: private static final Pattern FIELD_SEP = Pattern.compile("\u00ae"); 103: 104: /** 105: * Class for storing DateFormatSymbols data parsed from the property files. 106: */ 107: private static class DFSData 108: { 109: private String[] ampms; 110: private String[] eras; 111: private String localPatternChars; 112: private String[] months; 113: private String[] shortMonths; 114: private String[] weekdays; 115: private String[] shortWeekdays; 116: private String[] dateFormats; 117: private String[] timeFormats; 118: private String[][] runtimeZoneStrings; 119: 120: /** 121: * Construct a new instance with the parsed data. 122: * 123: * @param ampms strings for "am" and "pm". 124: * @param eras strings for calendar eras. 125: * @param localPatternChars localised pattern characters. 126: * @param months strings for the months of the year. 127: * @param shortMonths short strings for the months of the year. 128: * @param weekdays strings for the days of the week. 129: * @param shortWeekdays short strings for the days of the week. 130: * @param dateFormats localised date formats. 131: * @param timeFormats localised time formats. 132: * @param runtimeZoneStrings localised time zone names. 133: */ 134: public DFSData(String[] ampms, String[] eras, String localPatternChars, 135: String[] months, String[] shortMonths, String[] weekdays, 136: String[] shortWeekdays, String[] dateFormats, 137: String[] timeFormats, String[][] runtimeZoneStrings) 138: { 139: this.ampms = ampms; 140: this.eras = eras; 141: this.localPatternChars = localPatternChars; 142: this.months = months; 143: this.shortMonths = shortMonths; 144: this.weekdays = weekdays; 145: this.shortWeekdays = shortWeekdays; 146: this.dateFormats = dateFormats; 147: this.timeFormats = timeFormats; 148: this.runtimeZoneStrings = runtimeZoneStrings; 149: } 150: 151: /** 152: * Accessor for the AM/PM data. 153: * 154: * @return the AM/PM strings. 155: */ 156: public String[] getAMPMs() 157: { 158: return ampms.clone(); 159: } 160: 161: /** 162: * Accessor for the era data. 163: * 164: * @return the era strings. 165: */ 166: public String[] getEras() 167: { 168: return eras.clone(); 169: } 170: 171: /** 172: * Accessor for the local pattern characters. 173: * 174: * @return the local pattern characters. 175: */ 176: public String getLocalPatternChars() 177: { 178: return localPatternChars; 179: } 180: 181: /** 182: * Accessor for the months of the year (long form). 183: * 184: * @return the months of the year (long form). 185: */ 186: public String[] getMonths() 187: { 188: return months.clone(); 189: } 190: 191: /** 192: * Accessor for the months of the year (short form). 193: * 194: * @return the months of the year (short form). 195: */ 196: public String[] getShortMonths() 197: { 198: return shortMonths.clone(); 199: } 200: 201: /** 202: * Accessor for the days of the week (long form). 203: * 204: * @return the days of the week (long form). 205: */ 206: public String[] getWeekdays() 207: { 208: return weekdays.clone(); 209: } 210: 211: /** 212: * Accessor for the days of the week (short form). 213: * 214: * @return the days of the week (short form). 215: */ 216: public String[] getShortWeekdays() 217: { 218: return shortWeekdays.clone(); 219: } 220: 221: /** 222: * Accessor for the date formats. 223: * 224: * @return the date formats. 225: */ 226: public String[] getDateFormats() 227: { 228: return dateFormats.clone(); 229: } 230: 231: /** 232: * Accessor for the time formats. 233: * 234: * @return the time formats. 235: */ 236: public String[] getTimeFormats() 237: { 238: return timeFormats.clone(); 239: } 240: 241: /** 242: * Accessor for the zone strings. 243: * 244: * @return the zone strings. 245: */ 246: public String[][] getZoneStrings() 247: { 248: // Perform a deep clone so subarrays aren't modifiable 249: String[][] clone = runtimeZoneStrings.clone(); 250: for (int a = 0; a < clone.length; ++a) 251: clone[a] = runtimeZoneStrings[a].clone(); 252: return clone; 253: } 254: 255: } 256: 257: private static final ConcurrentMap<Locale, DFSData> dataCache = new ConcurrentHashMap<Locale, DFSData>(); 258: 259: String[] ampms; 260: String[] eras; 261: private String localPatternChars; 262: String[] months; 263: String[] shortMonths; 264: String[] shortWeekdays; 265: String[] weekdays; 266: 267: /** 268: * The timezone strings supplied by the runtime. 269: */ 270: private String[][] runtimeZoneStrings; 271: 272: /** 273: * Custom timezone strings supplied by {@link #setZoneStrings()}. 274: */ 275: private String[][] zoneStrings; 276: 277: private static final long serialVersionUID = -5987973545549424702L; 278: 279: // The order of these prefixes must be the same as in DateFormat 280: private static final String[] formatPrefixes = 281: { 282: "full", "long", "medium", "short" 283: }; 284: 285: // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL, 286: // and DEFAULT (constants defined in java.text.DateFormat). While 287: // not part of the official spec, we need a way to get at locale-specific 288: // default formatting patterns. They are declared package scope so 289: // as to be easily accessible where needed (DateFormat, SimpleDateFormat). 290: transient String[] dateFormats; 291: transient String[] timeFormats; 292: 293: /** 294: * Compiles a string array for a property using data from each of the locales in the 295: * hierarchy as necessary. 296: * 297: * @param bundles the locale hierarchy, starting with the most specific. 298: * @param name the name of the property. 299: * @param size the size the array should be when complete. 300: * @return a completed string array. 301: */ 302: private static String[] getStringArray(List<ResourceBundle> bundles, String name, int size) 303: { 304: return getStringArray(bundles, name, size, null); 305: } 306: 307: /** 308: * Compiles a string array for a property using data from each of the locales in the 309: * hierarchy as necessary. If non-null, the fallback array is also used for "sideways" 310: * inheritance (e.g. if there is no short name for a month, the long name is used rather 311: * than the empty string). 312: * 313: * @param bundles the locale hierarchy, starting with the most specific. 314: * @param name the name of the property. 315: * @param size the size the array should be when complete. 316: * @param fallback an array of long name fallback strings for data with both long and short names. 317: * @return a completed string array. 318: */ 319: private static String[] getStringArray(List<ResourceBundle> bundles, String name, int size, 320: String[] fallback) 321: { 322: String[] data = new String[size]; 323: Arrays.fill(data, ""); 324: // Populate array with data from each locale back to the root, starting with the most specific 325: for (int a = 0; a < bundles.size(); ++a) 326: { 327: String localeData = bundles.get(a).getString(name); 328: String[] array = FIELD_SEP.split(localeData, size); 329: for (int b = 0; b < data.length; ++b) 330: { 331: if (array.length > b && array[b] != null && data[b].isEmpty() && !array[b].isEmpty()) 332: data[b] = array[b]; 333: } 334: } 335: // Replace any remaining empty strings with data from the fallback array, if non-null 336: if (fallback != null && fallback.length == size) 337: { 338: for (int a = 0; a < data.length; ++a) 339: { 340: if (data[a].isEmpty() && fallback[a] != null && !fallback[a].isEmpty()) 341: data[a] = fallback[a]; 342: } 343: } 344: return data; 345: } 346: 347: private static String[][] getZoneStrings(List<ResourceBundle> bundles, Locale locale) 348: { 349: List<String[]> allZones = new ArrayList<String[]>(); 350: try 351: { 352: Map<String,String[]> systemZones = new HashMap<String,String[]>(); 353: for (ResourceBundle bundle : bundles) 354: { 355: String country = locale.getCountry(); 356: String data = bundle.getString("zoneStrings"); 357: String[] zones = ZONE_SEP.split(data); 358: for (int a = 0; a < zones.length; ++a) 359: { 360: String[] strings = FIELD_SEP.split(zones[a]); 361: String type = properties.getProperty(strings[0] + "." + country); 362: if (type == null) 363: type = properties.getProperty(strings[0] + ".DEFAULT"); 364: if (type != null) 365: strings[0] = type; 366: if (strings.length < 5) 367: { 368: String[] newStrings = new String[5]; 369: System.arraycopy(strings, 0, newStrings, 0, strings.length); 370: for (int b = strings.length; b < newStrings.length; ++b) 371: newStrings[b] = ""; 372: strings = newStrings; 373: } 374: String[] existing = systemZones.get(strings[0]); 375: if (existing != null && existing.length > 1) 376: { 377: for (int b = 1; b < existing.length; ++b) 378: if (!existing[b].equals("")) 379: strings[b] = existing[b]; 380: } 381: systemZones.put(strings[0], strings); 382: } 383: } 384: /* Final sanity check for missing values */ 385: for (String[] zstrings : systemZones.values()) 386: { 387: if (zstrings[1].equals("") && zstrings[2].equals("")) 388: { 389: for (Map.Entry<Object,Object> entry : properties.entrySet()) 390: { 391: String val = (String) entry.getValue(); 392: if (val.equals(zstrings[0])) 393: { 394: String key = (String) entry.getKey(); 395: String metazone = key.substring(0, key.indexOf(".")); 396: String type = properties.getProperty(metazone + "." + locale.getCountry()); 397: if (type == null) 398: type = properties.getProperty(metazone + ".DEFAULT"); 399: if (type != null) 400: { 401: String[] ostrings = systemZones.get(type); 402: zstrings[1] = ostrings[1]; 403: zstrings[2] = ostrings[2]; 404: } 405: } 406: } 407: } 408: } 409: allZones.addAll(systemZones.values()); 410: } 411: catch (MissingResourceException e) 412: { 413: /* This means runtime support for the locale 414: * is not available, so we just include providers. */ 415: } 416: for (TimeZoneNameProvider p : 417: ServiceLoader.load(TimeZoneNameProvider.class)) 418: { 419: for (Locale loc : p.getAvailableLocales()) 420: { 421: if (loc.equals(locale)) 422: { 423: for (String id : TimeZone.getAvailableIDs()) 424: { 425: String[] z = new String[5]; 426: z[0] = id; 427: z[1] = p.getDisplayName(id, false, 428: TimeZone.LONG, 429: locale); 430: z[2] = p.getDisplayName(id, false, 431: TimeZone.SHORT, 432: locale); 433: z[3] = p.getDisplayName(id, true, 434: TimeZone.LONG, 435: locale); 436: z[4] = p.getDisplayName(id, true, 437: TimeZone.SHORT, 438: locale); 439: allZones.add(z); 440: } 441: break; 442: } 443: } 444: } 445: return allZones.toArray(new String[allZones.size()][]); 446: } 447: 448: /** 449: * Retrieve the date or time formats for a specific key e.g. 450: * asking for "DateFormat" will return an array containing the 451: * full, long, medium and short date formats localised for 452: * the locales in the specified bundle. 453: * 454: * @param bundles the stack of bundles to check, most-specific first. 455: * @param key the type of format to retrieve. 456: * @param an array of localised strings for each format prefix. 457: */ 458: private static String[] formatsForKey(List<ResourceBundle> bundles, String key) 459: { 460: String[] values = new String[formatPrefixes.length]; 461: 462: for (int i = 0; i < formatPrefixes.length; i++) 463: values[i] = getString(bundles, formatPrefixes[i] + key); 464: 465: return values; 466: } 467: 468: /** 469: * Simple wrapper around extracting a {@code String} from a 470: * {@code ResourceBundle}. Keep searching less-specific locales 471: * until a non-null non-empty value is found. 472: * 473: * @param bundles the stack of bundles to check, most-specific first. 474: * @param key the key of the value to retrieve. 475: * @return the first non-null non-empty String found or the last 476: * retrieved if one isn't found. 477: */ 478: private static String getString(List<ResourceBundle> bundles, String key) 479: { 480: String val = null; 481: for (ResourceBundle bundle : bundles) 482: { 483: val = bundle.getString(key); 484: if (val != null && !val.isEmpty()) 485: return val; 486: } 487: return val; 488: } 489: 490: /** 491: * Retrieves the locale data from the property files and constructs a 492: * {@code DFSData} instance for it. 493: * 494: * @param the locale for which data should be retrieved. 495: * @return the parsed data. 496: * @throws MissingResourceException if the resources for the specified 497: * locale could not be found or loaded. 498: */ 499: private static DFSData retrieveData(Locale locale) 500: throws MissingResourceException 501: { 502: DFSData data = dataCache.get(locale); 503: if (data == null) 504: { 505: ClassLoader ldr = ClassLoader.getSystemClassLoader(); 506: List<ResourceBundle> bundles = new ArrayList<ResourceBundle>(); 507: ResourceBundle res 508: = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale, ldr); 509: bundles.add(res); 510: Locale resLocale = res.getLocale(); 511: while (resLocale != Locale.ROOT) 512: { 513: res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", 514: LocaleHelper.getFallbackLocale(resLocale), ldr); 515: bundles.add(res); 516: resLocale = res.getLocale(); 517: } 518: String[] lMonths = getStringArray(bundles, "months", 13); 519: String[] lWeekdays = getStringArray(bundles, "weekdays", 8); 520: data = new DFSData(getStringArray(bundles, "ampms", 2), 521: getStringArray(bundles, "eras", 2), 522: getString(bundles, "localPatternChars"), 523: lMonths, getStringArray(bundles, "shortMonths", 13, lMonths), 524: lWeekdays, getStringArray(bundles, "shortWeekdays", 8, lWeekdays), 525: formatsForKey(bundles, "DateFormat"), 526: formatsForKey(bundles, "TimeFormat"), 527: getZoneStrings(bundles, locale)); 528: DFSData cachedData = dataCache.putIfAbsent(locale, data); 529: // Use the earlier version if another thread beat us to it. 530: if (cachedData != null) 531: data = cachedData; 532: } 533: return data; 534: } 535: 536: /** 537: * This method initializes a new instance of <code>DateFormatSymbols</code> 538: * by loading the date format information for the specified locale. 539: * This constructor only obtains instances using the runtime's resources; 540: * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, 541: * call {@link #getInstance(java.util.Locale)} instead. 542: * 543: * @param locale The locale for which date formatting symbols should 544: * be loaded. 545: * @throws MissingResourceException if the resources for the specified 546: * locale could not be found or loaded. 547: * @see #getInstance(java.util.Locale) 548: */ 549: public DateFormatSymbols (Locale locale) 550: throws MissingResourceException 551: { 552: DFSData data = retrieveData(locale); 553: ampms = data.getAMPMs(); 554: eras = data.getEras(); 555: localPatternChars = data.getLocalPatternChars(); 556: months = data.getMonths(); 557: shortMonths = data.getShortMonths(); 558: weekdays = data.getWeekdays(); 559: shortWeekdays = data.getShortWeekdays(); 560: dateFormats = data.getDateFormats(); 561: timeFormats = data.getTimeFormats(); 562: runtimeZoneStrings = data.getZoneStrings(); 563: } 564: 565: /** 566: * This method loads the format symbol information for the default 567: * locale. This constructor only obtains instances using the runtime's resources; 568: * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, 569: * call {@link #getInstance()} instead. 570: * 571: * @throws MissingResourceException if the resources for the default 572: * locale could not be found or loaded. 573: * @see #getInstance() 574: */ 575: public DateFormatSymbols() 576: throws MissingResourceException 577: { 578: this (Locale.getDefault()); 579: } 580: 581: /** 582: * This method returns the list of strings used for displaying AM or PM. 583: * This is a two element <code>String</code> array indexed by 584: * <code>Calendar.AM</code> and <code>Calendar.PM</code> 585: * 586: * @return The list of AM/PM display strings. 587: */ 588: public String[] getAmPmStrings() 589: { 590: return ampms; 591: } 592: 593: /** 594: * This method returns the list of strings used for displaying eras 595: * (e.g., "BC" and "AD"). This is a two element <code>String</code> 596: * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 597: * 598: * @return The list of era disply strings. 599: */ 600: public String[] getEras() 601: { 602: return eras; 603: } 604: 605: /** 606: * This method returns the pattern character information for this 607: * object. This is an 18 character string that contains the characters 608: * that are used in creating the date formatting strings in 609: * <code>SimpleDateFormat</code>. The following are the character 610: * positions in the string and which format character they correspond 611: * to (the character in parentheses is the default value in the US English 612: * locale): 613: * <p> 614: * <ul> 615: * <li>0 - era (G)</li> 616: * <li>1 - year (y)</li> 617: * <li>2 - month (M)</li> 618: * <li>3 - day of month (d)</li> 619: * <li>4 - hour out of 12, from 1-12 (h)</li> 620: * <li>5 - hour out of 24, from 0-23 (H)</li> 621: * <li>6 - minute (m)</li> 622: * <li>7 - second (s)</li> 623: * <li>8 - millisecond (S)</li> 624: * <li>9 - date of week (E)</li> 625: * <li>10 - date of year (D)</li> 626: * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li> 627: * <li>12 - week in year (w)</li> 628: * <li>13 - week in month (W)</li> 629: * <li>14 - am/pm (a)</li> 630: * <li>15 - hour out of 24, from 1-24 (k)</li> 631: * <li>16 - hour out of 12, from 0-11 (K)</li> 632: * <li>17 - time zone (z)</li> 633: * </ul> 634: * 635: * @return The format patter characters 636: */ 637: public String getLocalPatternChars() 638: { 639: return localPatternChars; 640: } 641: 642: /** 643: * This method returns the list of strings used for displaying month 644: * names (e.g., "January" and "February"). This is a thirteen element 645: * string array indexed by <code>Calendar.JANUARY</code> through 646: * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen 647: * elements because some calendars have thriteen months. 648: * 649: * @return The list of month display strings. 650: */ 651: public String[] getMonths () 652: { 653: return months; 654: } 655: 656: /** 657: * This method returns the list of strings used for displaying abbreviated 658: * month names (e.g., "Jan" and "Feb"). This is a thirteen element 659: * <code>String</code> array indexed by <code>Calendar.JANUARY</code> 660: * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen 661: * elements because some calendars have thirteen months. 662: * 663: * @return The list of abbreviated month display strings. 664: */ 665: public String[] getShortMonths () 666: { 667: return shortMonths; 668: } 669: 670: /** 671: * This method returns the list of strings used for displaying abbreviated 672: * weekday names (e.g., "Sun" and "Mon"). This is an eight element 673: * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> 674: * through <code>Calendar.SATURDAY</code>. Note that the first element 675: * of this array is ignored. 676: * 677: * @return This list of abbreviated weekday display strings. 678: */ 679: public String[] getShortWeekdays () 680: { 681: return shortWeekdays; 682: } 683: 684: /** 685: * This method returns the list of strings used for displaying weekday 686: * names (e.g., "Sunday" and "Monday"). This is an eight element 687: * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> 688: * through <code>Calendar.SATURDAY</code>. Note that the first element 689: * of this array is ignored. 690: * 691: * @return This list of weekday display strings. 692: */ 693: public String[] getWeekdays () 694: { 695: return weekdays; 696: } 697: 698: /** 699: * This method returns this list of localized timezone display strings. 700: * This is a two dimensional <code>String</code> array where each row in 701: * the array contains five values: 702: * <P> 703: * <ul> 704: * <li>0 - The non-localized time zone id string.</li> 705: * <li>1 - The long name of the time zone (standard time).</li> 706: * <li>2 - The short name of the time zone (standard time).</li> 707: * <li>3 - The long name of the time zone (daylight savings time).</li> 708: * <li>4 - the short name of the time zone (daylight savings time).</li> 709: * </ul> 710: * <p> 711: * If {@link #setZoneStrings(String[][])} has been called, then the value 712: * passed to this will be returned. Otherwise the returned array contains 713: * zone names provided by the runtime environment and any 714: * {@link java.util.spi.TimeZoneProvider} instances. 715: * </p> 716: * 717: * @return The list of time zone display strings. 718: * @see #setZoneStrings(String[][]) 719: */ 720: public String[][] getZoneStrings() 721: { 722: if (zoneStrings != null) 723: return zoneStrings; 724: return runtimeZoneStrings; 725: } 726: 727: /** 728: * This method sets the list of strings used to display AM/PM values to 729: * the specified list. 730: * This is a two element <code>String</code> array indexed by 731: * <code>Calendar.AM</code> and <code>Calendar.PM</code> 732: * 733: * @param value The new list of AM/PM display strings. 734: */ 735: public void setAmPmStrings (String[] value) 736: { 737: if(value==null) 738: throw new NullPointerException(); 739: ampms = value; 740: } 741: 742: /** 743: * This method sets the list of strings used to display time eras to 744: * to the specified list. 745: * This is a two element <code>String</code> 746: * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 747: * 748: * @param labels The new list of era display strings. 749: */ 750: public void setEras (String[] labels) 751: { 752: if(labels==null) 753: throw new NullPointerException(); 754: eras = labels; 755: } 756: 757: /** 758: * This method sets the list of characters used to specific date/time 759: * formatting strings. 760: * This is an 18 character string that contains the characters 761: * that are used in creating the date formatting strings in 762: * <code>SimpleDateFormat</code>. The following are the character 763: * positions in the string and which format character they correspond 764: * to (the character in parentheses is the default value in the US English 765: * locale): 766: * <p> 767: * <ul> 768: * <li>0 - era (G)</li> 769: * <li>1 - year (y)</li> 770: * <li>2 - month (M)</li> 771: * <li>3 - day of month (d)</li> 772: * <li>4 - hour out of 12, from 1-12 (h)</li> 773: * <li>5 - hour out of 24, from 0-23 (H)</li> 774: * <li>6 - minute (m)</li> 775: * <li>7 - second (s)</li> 776: * <li>8 - millisecond (S)</li> 777: * <li>9 - date of week (E)</li> 778: * <li>10 - date of year (D)</li> 779: * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li> 780: * <li>12 - week in year (w)</li> 781: * <li>13 - week in month (W)</li> 782: * <li>14 - am/pm (a)</li> 783: * <li>15 - hour out of 24, from 1-24 (k)</li> 784: * <li>16 - hour out of 12, from 0-11 (K)</li> 785: * <li>17 - time zone (z)</li> 786: * </ul> 787: * 788: * @param chars The new format pattern characters 789: */ 790: public void setLocalPatternChars (String chars) 791: { 792: if(chars==null) 793: throw new NullPointerException(); 794: localPatternChars = chars; 795: } 796: 797: /** 798: * This method sets the list of strings used to display month names. 799: * This is a thirteen element 800: * string array indexed by <code>Calendar.JANUARY</code> through 801: * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen 802: * elements because some calendars have thriteen months. 803: * 804: * @param labels The list of month display strings. 805: */ 806: public void setMonths (String[] labels) 807: { 808: if(labels==null) 809: throw new NullPointerException(); 810: months = labels; 811: } 812: 813: /** 814: * This method sets the list of strings used to display abbreviated month 815: * names. 816: * This is a thirteen element 817: * <code>String</code> array indexed by <code>Calendar.JANUARY</code> 818: * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen 819: * elements because some calendars have thirteen months. 820: * 821: * @param labels The new list of abbreviated month display strings. 822: */ 823: public void setShortMonths (String[] labels) 824: { 825: if(labels==null) 826: throw new NullPointerException(); 827: shortMonths = labels; 828: } 829: 830: /** 831: * This method sets the list of strings used to display abbreviated 832: * weekday names. 833: * This is an eight element 834: * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> 835: * through <code>Calendar.SATURDAY</code>. Note that the first element 836: * of this array is ignored. 837: * 838: * @param labels This list of abbreviated weekday display strings. 839: */ 840: public void setShortWeekdays (String[] labels) 841: { 842: if(labels==null) 843: throw new NullPointerException(); 844: shortWeekdays = labels; 845: } 846: 847: /** 848: * This method sets the list of strings used to display weekday names. 849: * This is an eight element 850: * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> 851: * through <code>Calendar.SATURDAY</code>. Note that the first element 852: * of this array is ignored. 853: * 854: * @param labels This list of weekday display strings. 855: */ 856: public void setWeekdays (String[] labels) 857: { 858: if(labels==null) 859: throw new NullPointerException(); 860: weekdays = labels; 861: } 862: 863: /** 864: * This method sets the list of display strings for time zones. 865: * This is a two dimensional <code>String</code> array where each row in 866: * the array contains five values: 867: * <P> 868: * <ul> 869: * <li>0 - The non-localized time zone id string.</li> 870: * <li>1 - The long name of the time zone (standard time).</li> 871: * <li>2 - The short name of the time zone (standard time).</li> 872: * <li>3 - The long name of the time zone (daylight savings time).</li> 873: * <li>4 - the short name of the time zone (daylight savings time).</li> 874: * </ul> 875: * 876: * @params zones The list of time zone display strings. 877: */ 878: public void setZoneStrings (String[][] zones) 879: { 880: if(zones==null) 881: throw new NullPointerException(); 882: zoneStrings = zones; 883: } 884: 885: /* Does a "deep" equality test - recurses into arrays. */ 886: private static boolean equals (Object x, Object y) 887: { 888: if (x == y) 889: return true; 890: if (x == null || y == null) 891: return false; 892: if (! (x instanceof Object[]) || ! (y instanceof Object[])) 893: return x.equals(y); 894: Object[] xa = (Object[]) x; 895: Object[] ya = (Object[]) y; 896: if (xa.length != ya.length) 897: return false; 898: for (int i = xa.length; --i >= 0; ) 899: { 900: if (! equals(xa[i], ya[i])) 901: return false; 902: } 903: return true; 904: } 905: 906: private static int hashCode (Object x) 907: { 908: if (x == null) 909: return 0; 910: if (! (x instanceof Object[])) 911: return x.hashCode(); 912: Object[] xa = (Object[]) x; 913: int hash = 0; 914: for (int i = 0; i < xa.length; i++) 915: hash = 37 * hashCode(xa[i]); 916: return hash; 917: } 918: 919: /** 920: * This method tests a specified object for equality against this object. 921: * This will be true if and only if the specified object: 922: * <p> 923: * <ul> 924: * <li> Is not <code>null</code>.</li> 925: * <li> Is an instance of <code>DateFormatSymbols</code>.</li> 926: * <li> Contains identical formatting symbols to this object.</li> 927: * </ul> 928: * 929: * @param obj The <code>Object</code> to test for equality against. 930: * 931: * @return <code>true</code> if the specified object is equal to this one, 932: * <code>false</code> otherwise. 933: */ 934: public boolean equals (Object obj) 935: { 936: if (! (obj instanceof DateFormatSymbols)) 937: return false; 938: DateFormatSymbols other = (DateFormatSymbols) obj; 939: return (equals(ampms, other.ampms) 940: && equals(eras, other.eras) 941: && equals(localPatternChars, other.localPatternChars) 942: && equals(months, other.months) 943: && equals(shortMonths, other.shortMonths) 944: && equals(shortWeekdays, other.shortWeekdays) 945: && equals(weekdays, other.weekdays) 946: && equals(zoneStrings, other.zoneStrings)); 947: } 948: 949: /** 950: * Returns a new copy of this object. 951: * 952: * @return A copy of this object 953: */ 954: public Object clone () 955: { 956: try 957: { 958: return super.clone (); 959: } 960: catch (CloneNotSupportedException e) 961: { 962: return null; 963: } 964: } 965: 966: /** 967: * This method returns a hash value for this object. 968: * 969: * @return A hash value for this object. 970: */ 971: public int hashCode () 972: { 973: return (hashCode(ampms) 974: ^ hashCode(eras) 975: ^ hashCode(localPatternChars) 976: ^ hashCode(months) 977: ^ hashCode(shortMonths) 978: ^ hashCode(shortWeekdays) 979: ^ hashCode(weekdays) 980: ^ hashCode(zoneStrings)); 981: } 982: 983: /** 984: * Returns a {@link DateFormatSymbols} instance for the 985: * default locale obtained from either the runtime itself 986: * or one of the installed 987: * {@link java.text.spi.DateFormatSymbolsProvider} instances. 988: * This is equivalent to calling 989: * <code>getInstance(Locale.getDefault())</code>. 990: * 991: * @return a {@link DateFormatSymbols} instance for the default 992: * locale. 993: * @since 1.6 994: */ 995: public static final DateFormatSymbols getInstance() 996: { 997: return getInstance(Locale.getDefault()); 998: } 999: 1000: /** 1001: * Returns a {@link DateFormatSymbols} instance for the 1002: * specified locale obtained from either the runtime itself 1003: * or one of the installed 1004: * {@link java.text.spi.DateFormatSymbolsProvider} instances. 1005: * 1006: * @param locale the locale for which an instance should be 1007: * returned. 1008: * @return a {@link DateFormatSymbols} instance for the specified 1009: * locale. 1010: * @throws NullPointerException if <code>locale</code> is 1011: * <code>null</code>. 1012: * @since 1.6 1013: */ 1014: public static final DateFormatSymbols getInstance(Locale locale) 1015: { 1016: try 1017: { 1018: DateFormatSymbols syms = new DateFormatSymbols(locale); 1019: return syms; 1020: } 1021: catch (MissingResourceException e) 1022: { 1023: /* This means runtime support for the locale 1024: * is not available, so we check providers. */ 1025: } 1026: for (DateFormatSymbolsProvider p : 1027: ServiceLoader.load(DateFormatSymbolsProvider.class)) 1028: { 1029: for (Locale loc : p.getAvailableLocales()) 1030: { 1031: if (loc.equals(locale)) 1032: { 1033: DateFormatSymbols syms = p.getInstance(locale); 1034: if (syms != null) 1035: return syms; 1036: break; 1037: } 1038: } 1039: } 1040: return getInstance(LocaleHelper.getFallbackLocale(locale)); 1041: } 1042: 1043: }