1. /*
  2. * @(#)DateFormatSymbols.java 1.32 00/01/19
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. /*
  11. * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  12. * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  13. *
  14. * The original version of this source code and documentation is copyrighted
  15. * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  16. * materials are provided under terms of a License Agreement between Taligent
  17. * and Sun. This technology is protected by multiple US and International
  18. * patents. This notice and attribution to Taligent may not be removed.
  19. * Taligent is a registered trademark of Taligent, Inc.
  20. *
  21. */
  22. package java.text;
  23. import java.util.Locale;
  24. import java.util.ResourceBundle;
  25. import java.io.Serializable;
  26. import java.lang.ref.SoftReference;
  27. import java.util.Hashtable;
  28. import java.util.Vector;
  29. import java.util.Enumeration;
  30. /**
  31. * <code>DateFormatSymbols</code> is a public class for encapsulating
  32. * localizable date-time formatting data, such as the names of the
  33. * months, the names of the days of the week, and the time zone data.
  34. * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
  35. * <code>DateFormatSymbols</code> to encapsulate this information.
  36. *
  37. * <p>
  38. * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
  39. * Rather, you are encouraged to create a date-time formatter with the
  40. * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
  41. * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
  42. * These methods automatically create a <code>DateFormatSymbols</code> for
  43. * the formatter so that you don't have to. After the
  44. * formatter is created, you may modify its format pattern using the
  45. * <code>setPattern</code> method. For more information about
  46. * creating formatters using <code>DateFormat</code>'s factory methods,
  47. * see {@link DateFormat}.
  48. *
  49. * <p>
  50. * If you decide to create a date-time formatter with a specific
  51. * format pattern for a specific locale, you can do so with:
  52. * <blockquote>
  53. * <pre>
  54. * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
  55. * </pre>
  56. * </blockquote>
  57. *
  58. * <p>
  59. * <code>DateFormatSymbols</code> objects are clonable. When you obtain
  60. * a <code>DateFormatSymbols</code> object, feel free to modify the
  61. * date-time formatting data. For instance, you can replace the localized
  62. * date-time format pattern characters with the ones that you feel easy
  63. * to remember. Or you can change the representative cities
  64. * to your favorite ones.
  65. *
  66. * <p>
  67. * New <code>DateFormatSymbols</code> subclasses may be added to support
  68. * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
  69. * @see DateFormat
  70. * @see SimpleDateFormat
  71. * @see java.util.SimpleTimeZone
  72. * @version 1.32 01/19/00
  73. * @author Chen-Lieh Huang
  74. */
  75. public class DateFormatSymbols implements Serializable, Cloneable {
  76. /**
  77. * Construct a DateFormatSymbols object by loading format data from
  78. * resources for the default locale.
  79. *
  80. * @exception java.util.MissingResourceException
  81. * if the resources for the default locale cannot be
  82. * found or cannot be loaded.
  83. */
  84. public DateFormatSymbols()
  85. {
  86. initializeData(Locale.getDefault());
  87. }
  88. /**
  89. * Construct a DateFormatSymbols object by loading format data from
  90. * resources for the given locale.
  91. *
  92. * @exception java.util.MissingResourceException
  93. * if the resources for the specified locale cannot be
  94. * found or cannot be loaded.
  95. */
  96. public DateFormatSymbols(Locale locale)
  97. {
  98. initializeData(locale);
  99. }
  100. /**
  101. * Era strings. For example: "AD" and "BC". An array of 2 strings,
  102. * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
  103. * @serial
  104. */
  105. String eras[] = null;
  106. /**
  107. * Month strings. For example: "January", "February", etc. An array
  108. * of 13 strings (some calendars have 13 months), indexed by
  109. * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
  110. * @serial
  111. */
  112. String months[] = null;
  113. /**
  114. * Short month strings. For example: "Jan", "Feb", etc. An array of
  115. * 13 strings (some calendars have 13 months), indexed by
  116. * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
  117. * @serial
  118. */
  119. String shortMonths[] = null;
  120. /**
  121. * Weekday strings. For example: "Sunday", "Monday", etc. An array
  122. * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
  123. * <code>Calendar.MONDAY</code>, etc.
  124. * The element <code>weekdays[0]</code> is ignored.
  125. * @serial
  126. */
  127. String weekdays[] = null;
  128. /**
  129. * Short weekday strings. For example: "Sun", "Mon", etc. An array
  130. * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
  131. * <code>Calendar.MONDAY</code>, etc.
  132. * The element <code>shortWeekdays[0]</code> is ignored.
  133. * @serial
  134. */
  135. String shortWeekdays[] = null;
  136. /**
  137. * AM and PM strings. For example: "AM" and "PM". An array of
  138. * 2 strings, indexed by <code>Calendar.AM</code> and
  139. * <code>Calendar.PM</code>.
  140. * @serial
  141. */
  142. String ampms[] = null;
  143. /**
  144. * Localized names of time zones in this locale. This is a
  145. * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
  146. * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
  147. * entry containing the localized names for a single <code>TimeZone</code>.
  148. * Each such row contains (with <code>i</code> ranging from
  149. * 0..<em>n</em>-1):
  150. * <ul>
  151. * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
  152. * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
  153. * time</li>
  154. * <li><code>zoneStrings[i][2]</code> - short name of zone in
  155. * standard time</li>
  156. * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
  157. * savings time</li>
  158. * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
  159. * savings time</li>
  160. * </ul>
  161. * The zone ID is <em>not</em> localized; it corresponds to the ID
  162. * value associated with a system time zone object. All other entries
  163. * are localized names. If a zone does not implement daylight savings
  164. * time, the daylight savings time names are ignored.
  165. * @see java.text.resources.DateFormatZoneData
  166. * @see java.util.TimeZone
  167. * @serial
  168. */
  169. String zoneStrings[][] = null;
  170. /**
  171. * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
  172. * All locales use the same these unlocalized pattern characters.
  173. */
  174. static final String patternChars = "GyMdkHmsSEDFwWahKz";
  175. /**
  176. * Localized date-time pattern characters. For example, a locale may
  177. * wish to use 'u' rather than 'y' to represent years in its date format
  178. * pattern strings.
  179. * This string must be exactly 18 characters long, with the index of
  180. * the characters described by <code>DateFormat.ERA_FIELD</code>,
  181. * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were
  182. * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
  183. * @serial
  184. */
  185. String localPatternChars = null;
  186. /* use serialVersionUID from JDK 1.1.4 for interoperability */
  187. static final long serialVersionUID = -5987973545549424702L;
  188. /**
  189. * Gets era strings. For example: "AD" and "BC".
  190. * @return the era strings.
  191. */
  192. public String[] getEras() {
  193. return duplicate(eras);
  194. }
  195. /**
  196. * Sets era strings. For example: "AD" and "BC".
  197. * @param newEras the new era strings.
  198. */
  199. public void setEras(String[] newEras) {
  200. eras = duplicate(newEras);
  201. }
  202. /**
  203. * Gets month strings. For example: "January", "February", etc.
  204. * @return the month strings.
  205. */
  206. public String[] getMonths() {
  207. return duplicate(months);
  208. }
  209. /**
  210. * Sets month strings. For example: "January", "February", etc.
  211. * @param newMonths the new month strings.
  212. */
  213. public void setMonths(String[] newMonths) {
  214. months = duplicate(newMonths);
  215. }
  216. /**
  217. * Gets short month strings. For example: "Jan", "Feb", etc.
  218. * @return the short month strings.
  219. */
  220. public String[] getShortMonths() {
  221. return duplicate(shortMonths);
  222. }
  223. /**
  224. * Sets short month strings. For example: "Jan", "Feb", etc.
  225. * @param newShortMonths the new short month strings.
  226. */
  227. public void setShortMonths(String[] newShortMonths) {
  228. shortMonths = duplicate(newShortMonths);
  229. }
  230. /**
  231. * Gets weekday strings. For example: "Sunday", "Monday", etc.
  232. * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
  233. * <code>Calendar.MONDAY</code>, etc. to index the result array.
  234. */
  235. public String[] getWeekdays() {
  236. return duplicate(weekdays);
  237. }
  238. /**
  239. * Sets weekday strings. For example: "Sunday", "Monday", etc.
  240. * @param newWeekdays the new weekday strings. The array should
  241. * be indexed by <code>Calendar.SUNDAY</code>,
  242. * <code>Calendar.MONDAY</code>, etc.
  243. */
  244. public void setWeekdays(String[] newWeekdays) {
  245. weekdays = duplicate(newWeekdays);
  246. }
  247. /**
  248. * Gets short weekday strings. For example: "Sun", "Mon", etc.
  249. * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
  250. * <code>Calendar.MONDAY</code>, etc. to index the result array.
  251. */
  252. public String[] getShortWeekdays() {
  253. return duplicate(shortWeekdays);
  254. }
  255. /**
  256. * Sets short weekday strings. For example: "Sun", "Mon", etc.
  257. * @param newShortWeekdays the new short weekday strings. The array should
  258. * be indexed by <code>Calendar.SUNDAY</code>,
  259. * <code>Calendar.MONDAY</code>, etc.
  260. */
  261. public void setShortWeekdays(String[] newShortWeekdays) {
  262. shortWeekdays = duplicate(newShortWeekdays);
  263. }
  264. /**
  265. * Gets ampm strings. For example: "AM" and "PM".
  266. * @return the weekday strings.
  267. */
  268. public String[] getAmPmStrings() {
  269. return duplicate(ampms);
  270. }
  271. /**
  272. * Sets ampm strings. For example: "AM" and "PM".
  273. * @param newAmpms the new ampm strings.
  274. */
  275. public void setAmPmStrings(String[] newAmpms) {
  276. ampms = duplicate(newAmpms);
  277. }
  278. /**
  279. * Gets timezone strings.
  280. * @return the timezone strings.
  281. */
  282. public String[][] getZoneStrings() {
  283. String[][] aCopy = new String[zoneStrings.length][];
  284. for (int i = 0; i < zoneStrings.length; ++i)
  285. aCopy[i] = duplicate(zoneStrings[i]);
  286. return aCopy;
  287. }
  288. /**
  289. * Sets timezone strings.
  290. * @param newZoneStrings the new timezone strings.
  291. */
  292. public void setZoneStrings(String[][] newZoneStrings) {
  293. String[][] aCopy = new String[newZoneStrings.length][];
  294. for (int i = 0; i < newZoneStrings.length; ++i)
  295. aCopy[i] = duplicate(newZoneStrings[i]);
  296. zoneStrings = aCopy;
  297. }
  298. /**
  299. * Gets localized date-time pattern characters. For example: 'u', 't', etc.
  300. * @return the localized date-time pattern characters.
  301. */
  302. public String getLocalPatternChars() {
  303. return new String(localPatternChars);
  304. }
  305. /**
  306. * Sets localized date-time pattern characters. For example: 'u', 't', etc.
  307. * @param newLocalPatternChars the new localized date-time
  308. * pattern characters.
  309. */
  310. public void setLocalPatternChars(String newLocalPatternChars) {
  311. localPatternChars = new String(newLocalPatternChars);
  312. }
  313. /**
  314. * Overrides Cloneable
  315. */
  316. public Object clone()
  317. {
  318. try
  319. {
  320. DateFormatSymbols other = (DateFormatSymbols)super.clone();
  321. copyMembers(this, other);
  322. return other;
  323. } catch (CloneNotSupportedException e) {
  324. throw new InternalError();
  325. }
  326. }
  327. /**
  328. * Override hashCode.
  329. * Generates a hash code for the DateFormatSymbols object.
  330. */
  331. public int hashCode() {
  332. int hashcode = 0;
  333. for (int index = 0; index < this.zoneStrings[0].length; ++index)
  334. hashcode ^= this.zoneStrings[0][index].hashCode();
  335. return hashcode;
  336. }
  337. /**
  338. * Override equals
  339. */
  340. public boolean equals(Object obj)
  341. {
  342. if (this == obj) return true;
  343. if (obj == null || getClass() != obj.getClass()) return false;
  344. DateFormatSymbols that = (DateFormatSymbols) obj;
  345. return (Utility.arrayEquals(eras, that.eras)
  346. && Utility.arrayEquals(months, that.months)
  347. && Utility.arrayEquals(shortMonths, that.shortMonths)
  348. && Utility.arrayEquals(weekdays, that.weekdays)
  349. && Utility.arrayEquals(shortWeekdays, that.shortWeekdays)
  350. && Utility.arrayEquals(ampms, that.ampms)
  351. && Utility.arrayEquals(zoneStrings, that.zoneStrings)
  352. && Utility.arrayEquals(localPatternChars,
  353. that.localPatternChars));
  354. }
  355. // =======================privates===============================
  356. /**
  357. * Useful constant for defining timezone offsets.
  358. */
  359. static final int millisPerHour = 60*60*1000;
  360. /**
  361. * Cache to hold the LocaleElements and DateFormatZoneData ResourceBundles
  362. * of a Locale.
  363. */
  364. private static Hashtable cachedLocaleData = new Hashtable(3);
  365. /**
  366. * cache to hold time zone localized strings. Keyed by Locale
  367. */
  368. private static Hashtable cachedZoneData = new Hashtable();
  369. /* Utility methods for fetching resource bundles */
  370. private ResourceBundle getLocaleElements(Locale desiredLocale) {
  371. return ResourceBundle.getBundle("java.text.resources.LocaleElements",
  372. desiredLocale);
  373. }
  374. private ResourceBundle getZoneData(Locale desiredLocale) {
  375. return ResourceBundle.getBundle("java.text.resources.DateFormatZoneData",
  376. desiredLocale);
  377. }
  378. /**
  379. * Look up resource data for the desiredLocale in the cache; update the
  380. * cache if necessary.
  381. */
  382. private ResourceBundle[] cacheLookup(Locale desiredLocale) {
  383. ResourceBundle[] rbs = new ResourceBundle[2];
  384. SoftReference[] data
  385. = (SoftReference[])cachedLocaleData.get(desiredLocale);
  386. if (data == null) {
  387. rbs[0] = getLocaleElements(desiredLocale);
  388. rbs[1] = getZoneData(desiredLocale);
  389. data = new SoftReference[] { new SoftReference(rbs[0]),
  390. new SoftReference(rbs[1]) };
  391. cachedLocaleData.put(desiredLocale, data);
  392. } else {
  393. ResourceBundle r;
  394. if ((r = (ResourceBundle)data[0].get()) == null) {
  395. r = getLocaleElements(desiredLocale);
  396. data[0] = new SoftReference(r);
  397. }
  398. rbs[0] = r;
  399. if ((r = (ResourceBundle)data[1].get()) == null) {
  400. r = getZoneData(desiredLocale);
  401. data[1] = new SoftReference(r);
  402. }
  403. rbs[1] = r;
  404. }
  405. return rbs;
  406. }
  407. /**
  408. * Load time zone localized strings. Enumerate all keys (except
  409. * "localPatternChars" and "zoneStrings").
  410. */
  411. private String[][] loadZoneStrings(Locale desiredLocale,
  412. ResourceBundle rsrc)
  413. {
  414. String[][] zones;
  415. SoftReference data = (SoftReference)cachedZoneData.get(desiredLocale);
  416. if (data == null || ((zones = (String[][])data.get()) == null)) {
  417. Vector vec = new Vector();
  418. Enumeration keys = rsrc.getKeys();
  419. while(keys.hasMoreElements()) {
  420. String key = (String)keys.nextElement();
  421. if (!key.equals("localPatternChars") &&
  422. !key.equals("zoneStrings")) {
  423. vec.add(rsrc.getObject(key));
  424. }
  425. }
  426. zones = new String[vec.size()][];
  427. vec.toArray(zones);
  428. data = new SoftReference(zones);
  429. cachedZoneData.put(desiredLocale, data);
  430. }
  431. return zones;
  432. }
  433. private void initializeData(Locale desiredLocale)
  434. {
  435. int i;
  436. ResourceBundle[] rbs = cacheLookup(desiredLocale);
  437. ResourceBundle resource = rbs[0];
  438. ResourceBundle zoneResource = rbs[1];
  439. // FIXME: cache only ResourceBundle. Hence every time, will do
  440. // getObject(). This won't be necessary if the Resource itself
  441. // is cached.
  442. eras = (String[])resource.getObject("Eras");
  443. months = resource.getStringArray("MonthNames");
  444. shortMonths = resource.getStringArray("MonthAbbreviations");
  445. String[] lWeekdays = resource.getStringArray("DayNames");
  446. weekdays = new String[8];
  447. weekdays[0] = ""; // 1-based
  448. for (i=0; i<lWeekdays.length; i++)
  449. weekdays[i+1] = lWeekdays[i];
  450. String[] sWeekdays = resource.getStringArray("DayAbbreviations");
  451. shortWeekdays = new String[8];
  452. shortWeekdays[0] = ""; // 1-based
  453. for (i=0; i<sWeekdays.length; i++)
  454. shortWeekdays[i+1] = sWeekdays[i];
  455. ampms = resource.getStringArray("AmPmMarkers");
  456. zoneStrings = (String[][])loadZoneStrings(desiredLocale,
  457. zoneResource);
  458. localPatternChars
  459. = (String) zoneResource.getObject("localPatternChars");
  460. }
  461. /**
  462. * Package private: used by SimpleDateFormat
  463. * Gets the index for the given time zone ID to obtain the timezone
  464. * strings for formatting. The time zone ID is just for programmatic
  465. * lookup. NOT LOCALIZED!!!
  466. * @param ID the given time zone ID.
  467. * @return the index of the given time zone ID. Returns -1 if
  468. * the given time zone ID can't be located in the DateFormatSymbols object.
  469. * @see java.util.SimpleTimeZone
  470. */
  471. final int getZoneIndex (String ID)
  472. {
  473. for (int index=0; index<zoneStrings.length; index++)
  474. {
  475. if (ID.equalsIgnoreCase(zoneStrings[index][0])) return index;
  476. }
  477. return -1;
  478. }
  479. /**
  480. * Clones an array of Strings.
  481. * @param srcArray the source array to be cloned.
  482. * @param count the number of elements in the given source array.
  483. * @return a cloned array.
  484. */
  485. private final String[] duplicate(String[] srcArray)
  486. {
  487. String[] dstArray = new String[srcArray.length];
  488. System.arraycopy(srcArray, 0, dstArray, 0, srcArray.length);
  489. return dstArray;
  490. }
  491. /**
  492. * Clones all the data members from the source DateFormatSymbols to
  493. * the target DateFormatSymbols. This is only for subclasses.
  494. * @param src the source DateFormatSymbols.
  495. * @param dst the target DateFormatSymbols.
  496. */
  497. private final void copyMembers(DateFormatSymbols src, DateFormatSymbols dst)
  498. {
  499. dst.eras = duplicate(src.eras);
  500. dst.months = duplicate(src.months);
  501. dst.shortMonths = duplicate(src.shortMonths);
  502. dst.weekdays = duplicate(src.weekdays);
  503. dst.shortWeekdays = duplicate(src.shortWeekdays);
  504. dst.ampms = duplicate(src.ampms);
  505. for (int i = 0; i < dst.zoneStrings.length; ++i)
  506. dst.zoneStrings[i] = duplicate(src.zoneStrings[i]);
  507. dst.localPatternChars = new String (src.localPatternChars);
  508. }
  509. /**
  510. * Compares the equality of the two arrays of String.
  511. * @param current this String array.
  512. * @param other that String array.
  513. */
  514. private final boolean equals(String[] current, String[] other)
  515. {
  516. int count = current.length;
  517. for (int i = 0; i < count; ++i)
  518. if (!current[i].equals(other[i]))
  519. return false;
  520. return true;
  521. }
  522. }