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