001: /*
002: *
003: * @(#)DateFormatSymbols.java 1.41 06/10/10
004: *
005: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights
006: * Reserved. Use is subject to license terms.
007: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License version
011: * 2 only, as published by the Free Software Foundation.
012: *
013: * This program is distributed in the hope that it will be useful, but
014: * WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * General Public License version 2 for more details (a copy is
017: * included at /legal/license.txt).
018: *
019: * You should have received a copy of the GNU General Public License
020: * version 2 along with this work; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022: * 02110-1301 USA
023: *
024: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
025: * Clara, CA 95054 or visit www.sun.com if you need additional
026: * information or have any questions.
027: */
028:
029: /*
030: * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
031: * (C) Copyright IBM Corp. 1996 - All Rights Reserved
032: *
033: * The original version of this source code and documentation is copyrighted
034: * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
035: * materials are provided under terms of a License Agreement between Taligent
036: * and Sun. This technology is protected by multiple US and International
037: * patents. This notice and attribution to Taligent may not be removed.
038: * Taligent is a registered trademark of Taligent, Inc.
039: *
040: */
041:
042: package java.text;
043:
044: import java.util.Locale;
045: import java.util.ResourceBundle;
046: import java.io.Serializable;
047: import java.lang.ref.SoftReference;
048: import java.util.Vector;
049: import java.util.Enumeration;
050: import sun.text.Utility;
051: import sun.text.resources.LocaleData;
052: import java.util.Hashtable;
053:
054: /**
055: * <code>DateFormatSymbols</code> is a public class for encapsulating
056: * localizable date-time formatting data, such as the names of the
057: * months, the names of the days of the week, and the time zone data.
058: * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
059: * <code>DateFormatSymbols</code> to encapsulate this information.
060: *
061: * <p>
062: * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
063: * Rather, you are encouraged to create a date-time formatter with the
064: * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
065: * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
066: * These methods automatically create a <code>DateFormatSymbols</code> for
067: * the formatter so that you don't have to. After the
068: * formatter is created, you may modify its format pattern using the
069: * <code>setPattern</code> method. For more information about
070: * creating formatters using <code>DateFormat</code>'s factory methods,
071: * see {@link DateFormat}.
072: *
073: * <p>
074: * If you decide to create a date-time formatter with a specific
075: * format pattern for a specific locale, you can do so with:
076: * <blockquote>
077: * <pre>
078: * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
079: * </pre>
080: * </blockquote>
081: *
082: * <p>
083: * <code>DateFormatSymbols</code> objects are cloneable. When you obtain
084: * a <code>DateFormatSymbols</code> object, feel free to modify the
085: * date-time formatting data. For instance, you can replace the localized
086: * date-time format pattern characters with the ones that you feel easy
087: * to remember. Or you can change the representative cities
088: * to your favorite ones.
089: *
090: * <p>
091: * New <code>DateFormatSymbols</code> subclasses may be added to support
092: * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
093:
094: * @see DateFormat
095: * @see SimpleDateFormat
096: * @see java.util.SimpleTimeZone
097: * @version 1.41, 10/10/06
098: * @author Chen-Lieh Huang
099: */
100: public class DateFormatSymbols implements Serializable, Cloneable {
101:
102: /**
103: * Construct a DateFormatSymbols object by loading format data from
104: * resources for the default locale.
105: *
106: * @exception java.util.MissingResourceException
107: * if the resources for the default locale cannot be
108: * found or cannot be loaded.
109: */
110: public DateFormatSymbols() {
111: initializeData(Locale.getDefault());
112: }
113:
114: /**
115: * Construct a DateFormatSymbols object by loading format data from
116: * resources for the given locale.
117: *
118: * @exception java.util.MissingResourceException
119: * if the resources for the specified locale cannot be
120: * found or cannot be loaded.
121: */
122: public DateFormatSymbols(Locale locale) {
123: initializeData(locale);
124: }
125:
126: /**
127: * Era strings. For example: "AD" and "BC". An array of 2 strings,
128: * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
129: * @serial
130: */
131: String eras[] = null;
132:
133: /**
134: * Month strings. For example: "January", "February", etc. An array
135: * of 13 strings (some calendars have 13 months), indexed by
136: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
137: * @serial
138: */
139: String months[] = null;
140:
141: /**
142: * Short month strings. For example: "Jan", "Feb", etc. An array of
143: * 13 strings (some calendars have 13 months), indexed by
144: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
145:
146: * @serial
147: */
148: String shortMonths[] = null;
149:
150: /**
151: * Weekday strings. For example: "Sunday", "Monday", etc. An array
152: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
153: * <code>Calendar.MONDAY</code>, etc.
154: * The element <code>weekdays[0]</code> is ignored.
155: * @serial
156: */
157: String weekdays[] = null;
158:
159: /**
160: * Short weekday strings. For example: "Sun", "Mon", etc. An array
161: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
162: * <code>Calendar.MONDAY</code>, etc.
163: * The element <code>shortWeekdays[0]</code> is ignored.
164: * @serial
165: */
166: String shortWeekdays[] = null;
167:
168: /**
169: * AM and PM strings. For example: "AM" and "PM". An array of
170: * 2 strings, indexed by <code>Calendar.AM</code> and
171: * <code>Calendar.PM</code>.
172: * @serial
173: */
174: String ampms[] = null;
175:
176: /**
177: * Localized names of time zones in this locale. This is a
178: * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
179: * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
180: * entry containing the localized names for a single <code>TimeZone</code>.
181: * Each such row contains (with <code>i</code> ranging from
182: * 0..<em>n</em>-1):
183: * <ul>
184: * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
185: * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
186: * time</li>
187: * <li><code>zoneStrings[i][2]</code> - short name of zone in
188: * standard time</li>
189: * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
190: * savings time</li>
191: * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
192: * savings time</li>
193: * </ul>
194: * The zone ID is <em>not</em> localized; it corresponds to the ID
195: * value associated with a system time zone object. All other entries
196: * are localized names. If a zone does not implement daylight savings
197: * time, the daylight savings time names are ignored.
198: * @see java.util.TimeZone
199: * @serial
200: */
201: String zoneStrings[][] = null;
202:
203: /**
204: * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
205: * All locales use the same these unlocalized pattern characters.
206: */
207: static final String patternChars = "GyMdkHmsSEDFwWahKzZ";
208:
209: /**
210: * Localized date-time pattern characters. For example, a locale may
211: * wish to use 'u' rather than 'y' to represent years in its date format
212: * pattern strings.
213: * This string must be exactly 18 characters long, with the index of
214: * the characters described by <code>DateFormat.ERA_FIELD</code>,
215: * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were
216: * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
217: * @serial
218: */
219: String localPatternChars = null;
220:
221: /* use serialVersionUID from JDK 1.1.4 for interoperability */
222: static final long serialVersionUID = -5987973545549424702L;
223:
224: /**
225: * Gets era strings. For example: "AD" and "BC".
226: * @return the era strings.
227: */
228: public String[] getEras() {
229: return duplicate(eras);
230: }
231:
232: /**
233: * Sets era strings. For example: "AD" and "BC".
234: * @param newEras the new era strings.
235: */
236: public void setEras(String[] newEras) {
237: eras = duplicate(newEras);
238: }
239:
240: /**
241: * Gets month strings. For example: "January", "February", etc.
242: * @return the month strings.
243: */
244: public String[] getMonths() {
245: return duplicate(months);
246: }
247:
248: /**
249: * Sets month strings. For example: "January", "February", etc.
250: * @param newMonths the new month strings.
251: */
252: public void setMonths(String[] newMonths) {
253: months = duplicate(newMonths);
254: }
255:
256: /**
257: * Gets short month strings. For example: "Jan", "Feb", etc.
258: * @return the short month strings.
259: */
260: public String[] getShortMonths() {
261: return duplicate(shortMonths);
262: }
263:
264: /**
265: * Sets short month strings. For example: "Jan", "Feb", etc.
266: * @param newShortMonths the new short month strings.
267: */
268: public void setShortMonths(String[] newShortMonths) {
269: shortMonths = duplicate(newShortMonths);
270: }
271:
272: /**
273: * Gets weekday strings. For example: "Sunday", "Monday", etc.
274: * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
275: * <code>Calendar.MONDAY</code>, etc. to index the result array.
276: */
277: public String[] getWeekdays() {
278: return duplicate(weekdays);
279: }
280:
281: /**
282: * Sets weekday strings. For example: "Sunday", "Monday", etc.
283: * @param newWeekdays the new weekday strings. The array should
284: * be indexed by <code>Calendar.SUNDAY</code>,
285: * <code>Calendar.MONDAY</code>, etc.
286: */
287: public void setWeekdays(String[] newWeekdays) {
288: weekdays = duplicate(newWeekdays);
289: }
290:
291: /**
292: * Gets short weekday strings. For example: "Sun", "Mon", etc.
293: * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
294: * <code>Calendar.MONDAY</code>, etc. to index the result array.
295: */
296: public String[] getShortWeekdays() {
297: return duplicate(shortWeekdays);
298: }
299:
300: /**
301: * Sets short weekday strings. For example: "Sun", "Mon", etc.
302: * @param newShortWeekdays the new short weekday strings. The array should
303: * be indexed by <code>Calendar.SUNDAY</code>,
304: * <code>Calendar.MONDAY</code>, etc.
305: */
306: public void setShortWeekdays(String[] newShortWeekdays) {
307: shortWeekdays = duplicate(newShortWeekdays);
308: }
309:
310: /**
311: * Gets ampm strings. For example: "AM" and "PM".
312: * @return the ampm strings.
313: */
314: public String[] getAmPmStrings() {
315: return duplicate(ampms);
316: }
317:
318: /**
319: * Sets ampm strings. For example: "AM" and "PM".
320: * @param newAmpms the new ampm strings.
321: */
322: public void setAmPmStrings(String[] newAmpms) {
323: ampms = duplicate(newAmpms);
324: }
325:
326: /**
327: * Gets timezone strings.
328: * @return the timezone strings.
329: */
330: public String[][] getZoneStrings() {
331: String[][] aCopy = new String[zoneStrings.length][];
332: for (int i = 0; i < zoneStrings.length; ++i)
333: aCopy[i] = duplicate(zoneStrings[i]);
334: return aCopy;
335: }
336:
337: /**
338: * Sets timezone strings.
339: * @param newZoneStrings the new timezone strings.
340: */
341: public void setZoneStrings(String[][] newZoneStrings) {
342: String[][] aCopy = new String[newZoneStrings.length][];
343: for (int i = 0; i < newZoneStrings.length; ++i)
344: aCopy[i] = duplicate(newZoneStrings[i]);
345: zoneStrings = aCopy;
346: }
347:
348: /**
349: * Gets localized date-time pattern characters. For example: 'u', 't', etc.
350: * @return the localized date-time pattern characters.
351: */
352: public String getLocalPatternChars() {
353: return new String(localPatternChars);
354: }
355:
356: /**
357: * Sets localized date-time pattern characters. For example: 'u', 't', etc.
358: * @param newLocalPatternChars the new localized date-time
359: * pattern characters.
360: */
361: public void setLocalPatternChars(String newLocalPatternChars) {
362: localPatternChars = new String(newLocalPatternChars);
363: }
364:
365: /**
366: * Overrides Cloneable
367: */
368: public Object clone() {
369: try {
370: DateFormatSymbols other = (DateFormatSymbols) super .clone();
371: copyMembers(this , other);
372: return other;
373: } catch (CloneNotSupportedException e) {
374: throw new InternalError();
375: }
376: }
377:
378: /**
379: * Override hashCode.
380: * Generates a hash code for the DateFormatSymbols object.
381: */
382: public int hashCode() {
383: int hashcode = 0;
384: for (int index = 0; index < this .zoneStrings[0].length; ++index)
385: hashcode ^= this .zoneStrings[0][index].hashCode();
386: return hashcode;
387: }
388:
389: /**
390: * Override equals
391: */
392: public boolean equals(Object obj) {
393: if (this == obj)
394: return true;
395: if (obj == null || getClass() != obj.getClass())
396: return false;
397: DateFormatSymbols that = (DateFormatSymbols) obj;
398: return (Utility.arrayEquals(eras, that.eras)
399: && Utility.arrayEquals(months, that.months)
400: && Utility.arrayEquals(shortMonths, that.shortMonths)
401: && Utility.arrayEquals(weekdays, that.weekdays)
402: && Utility.arrayEquals(shortWeekdays,
403: that.shortWeekdays)
404: && Utility.arrayEquals(ampms, that.ampms)
405: && Utility.arrayEquals(zoneStrings, that.zoneStrings) && Utility
406: .arrayEquals(localPatternChars, that.localPatternChars));
407: }
408:
409: // =======================privates===============================
410:
411: /**
412: * Useful constant for defining timezone offsets.
413: */
414: static final int millisPerHour = 60 * 60 * 1000;
415:
416: /**
417: * Cache to hold the LocaleElements and DateFormatZoneData ResourceBundles
418: * of a Locale.
419: */
420: private static Hashtable cachedLocaleData = new Hashtable(3);
421:
422: /**
423: * cache to hold time zone localized strings. Keyed by Locale
424: */
425: private static Hashtable cachedZoneData = new Hashtable();
426:
427: /**
428: * Look up resource data for the desiredLocale in the cache; update the
429: * cache if necessary.
430: */
431: private ResourceBundle[] cacheLookup(Locale desiredLocale) {
432: ResourceBundle[] rbs = new ResourceBundle[2];
433: SoftReference[] data = (SoftReference[]) cachedLocaleData
434: .get(desiredLocale);
435: if (data == null) {
436: rbs[0] = LocaleData.getLocaleElements(desiredLocale);
437: rbs[1] = LocaleData.getDateFormatZoneData(desiredLocale);
438: data = new SoftReference[] { new SoftReference(rbs[0]),
439: new SoftReference(rbs[1]) };
440: cachedLocaleData.put(desiredLocale, data);
441: } else {
442: ResourceBundle r;
443: if ((r = (ResourceBundle) data[0].get()) == null) {
444: r = LocaleData.getLocaleElements(desiredLocale);
445: data[0] = new SoftReference(r);
446: }
447: rbs[0] = r;
448: if ((r = (ResourceBundle) data[1].get()) == null) {
449: r = LocaleData.getDateFormatZoneData(desiredLocale);
450: data[1] = new SoftReference(r);
451: }
452: rbs[1] = r;
453: }
454: return rbs;
455: }
456:
457: /**
458: * Load time zone localized strings. Enumerate all keys (except
459: * "localPatternChars" and "zoneStrings").
460: */
461: private String[][] loadZoneStrings(Locale desiredLocale,
462: ResourceBundle rsrc) {
463: String[][] zones;
464: SoftReference data = (SoftReference) cachedZoneData
465: .get(desiredLocale);
466: if (data == null || ((zones = (String[][]) data.get()) == null)) {
467: Vector vec = new Vector();
468: Enumeration keys = rsrc.getKeys();
469: while (keys.hasMoreElements()) {
470: String key = (String) keys.nextElement();
471: if (!key.equals("localPatternChars")
472: && !key.equals("zoneStrings")) {
473: vec.add(rsrc.getObject(key));
474: }
475: }
476: zones = new String[vec.size()][];
477: vec.toArray(zones);
478: data = new SoftReference(zones);
479: cachedZoneData.put(desiredLocale, data);
480: }
481: return zones;
482: }
483:
484: private void initializeData(Locale desiredLocale) {
485: int i;
486: ResourceBundle[] rbs = cacheLookup(desiredLocale);
487: ResourceBundle resource = rbs[0];
488: ResourceBundle zoneResource = rbs[1];
489:
490: // Cache only ResourceBundle. Hence every time, will do
491: // getObject(). This won't be necessary if the Resource itself
492: // is cached.
493: eras = (String[]) resource.getObject("Eras");
494: months = resource.getStringArray("MonthNames");
495: shortMonths = resource.getStringArray("MonthAbbreviations");
496: String[] lWeekdays = resource.getStringArray("DayNames");
497: weekdays = new String[8];
498: weekdays[0] = ""; // 1-based
499: for (i = 0; i < lWeekdays.length; i++)
500: weekdays[i + 1] = lWeekdays[i];
501: String[] sWeekdays = resource
502: .getStringArray("DayAbbreviations");
503: shortWeekdays = new String[8];
504: shortWeekdays[0] = ""; // 1-based
505: for (i = 0; i < sWeekdays.length; i++)
506: shortWeekdays[i + 1] = sWeekdays[i];
507: ampms = resource.getStringArray("AmPmMarkers");
508: zoneStrings = (String[][]) loadZoneStrings(desiredLocale,
509: zoneResource);
510: localPatternChars = (String) zoneResource
511: .getObject("localPatternChars");
512: }
513:
514: /**
515: * Package private: used by SimpleDateFormat
516: * Gets the index for the given time zone ID to obtain the timezone
517: * strings for formatting. The time zone ID is just for programmatic
518: * lookup. NOT LOCALIZED!!!
519: * @param ID the given time zone ID.
520: * @return the index of the given time zone ID. Returns -1 if
521: * the given time zone ID can't be located in the DateFormatSymbols object.
522: * @see java.util.SimpleTimeZone
523: */
524: final int getZoneIndex(String ID) {
525: for (int index = 0; index < zoneStrings.length; index++) {
526: if (ID.equalsIgnoreCase(zoneStrings[index][0]))
527: return index;
528: }
529:
530: return -1;
531: }
532:
533: /**
534: * Clones an array of Strings.
535: * @param srcArray the source array to be cloned.
536: * @param count the number of elements in the given source array.
537: * @return a cloned array.
538: */
539: private final String[] duplicate(String[] srcArray) {
540: String[] dstArray = new String[srcArray.length];
541: System.arraycopy(srcArray, 0, dstArray, 0, srcArray.length);
542: return dstArray;
543: }
544:
545: /**
546: * Clones all the data members from the source DateFormatSymbols to
547: * the target DateFormatSymbols. This is only for subclasses.
548: * @param src the source DateFormatSymbols.
549: * @param dst the target DateFormatSymbols.
550: */
551: private final void copyMembers(DateFormatSymbols src,
552: DateFormatSymbols dst) {
553: dst.eras = duplicate(src.eras);
554: dst.months = duplicate(src.months);
555: dst.shortMonths = duplicate(src.shortMonths);
556: dst.weekdays = duplicate(src.weekdays);
557: dst.shortWeekdays = duplicate(src.shortWeekdays);
558: dst.ampms = duplicate(src.ampms);
559: for (int i = 0; i < dst.zoneStrings.length; ++i)
560: dst.zoneStrings[i] = duplicate(src.zoneStrings[i]);
561: dst.localPatternChars = new String(src.localPatternChars);
562: }
563:
564: /**
565: * Compares the equality of the two arrays of String.
566: * @param current this String array.
567: * @param other that String array.
568: */
569: private final boolean equals(String[] current, String[] other) {
570: int count = current.length;
571:
572: for (int i = 0; i < count; ++i)
573: if (!current[i].equals(other[i]))
574: return false;
575: return true;
576: }
577:
578: }
|