0001: /*
0002: *******************************************************************************
0003: * Copyright (C) 1996-2006, International Business Machines Corporation and *
0004: * others. All Rights Reserved. *
0005: *******************************************************************************
0006: */
0007:
0008: package com.ibm.icu.text;
0009:
0010: import com.ibm.icu.impl.ICUResourceBundle;
0011: import com.ibm.icu.impl.CalendarData;
0012: import com.ibm.icu.impl.TextTrieMap;
0013: import com.ibm.icu.impl.Utility;
0014: import com.ibm.icu.util.Calendar;
0015: import com.ibm.icu.util.GregorianCalendar;
0016: import com.ibm.icu.util.TimeZone;
0017: import com.ibm.icu.util.UResourceBundle;
0018: import com.ibm.icu.impl.ZoneMeta;
0019: import com.ibm.icu.util.ULocale;
0020: import com.ibm.icu.impl.SoftCache;
0021:
0022: import java.io.Serializable;
0023: import java.util.ArrayList;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.Locale;
0028: import java.util.MissingResourceException;
0029: import java.util.ResourceBundle;
0030:
0031: /**
0032: * <code>DateFormatSymbols</code> is a public class for encapsulating
0033: * localizable date-time formatting data, such as the names of the
0034: * months, the names of the days of the week, and the time zone data.
0035: * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
0036: * <code>DateFormatSymbols</code> to encapsulate this information.
0037: *
0038: * <p>
0039: * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
0040: * Rather, you are encouraged to create a date-time formatter with the
0041: * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
0042: * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
0043: * These methods automatically create a <code>DateFormatSymbols</code> for
0044: * the formatter so that you don't have to. After the
0045: * formatter is created, you may modify its format pattern using the
0046: * <code>setPattern</code> method. For more information about
0047: * creating formatters using <code>DateFormat</code>'s factory methods,
0048: * see {@link DateFormat}.
0049: *
0050: * <p>
0051: * If you decide to create a date-time formatter with a specific
0052: * format pattern for a specific locale, you can do so with:
0053: * <blockquote>
0054: * <pre>
0055: * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
0056: * </pre>
0057: * </blockquote>
0058: *
0059: * <p>
0060: * <code>DateFormatSymbols</code> objects are clonable. When you obtain
0061: * a <code>DateFormatSymbols</code> object, feel free to modify the
0062: * date-time formatting data. For instance, you can replace the localized
0063: * date-time format pattern characters with the ones that you feel easy
0064: * to remember. Or you can change the representative cities
0065: * to your favorite ones.
0066: *
0067: * <p>
0068: * New <code>DateFormatSymbols</code> subclasses may be added to support
0069: * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
0070:
0071: * @see DateFormat
0072: * @see SimpleDateFormat
0073: * @see com.ibm.icu.util.SimpleTimeZone
0074: * @author Chen-Lieh Huang
0075: * @stable ICU 2.0
0076: */
0077: public class DateFormatSymbols implements Serializable, Cloneable {
0078:
0079: // TODO make sure local pattern char string is 18 characters long,
0080: // that is, that it encompasses the new 'u' char for
0081: // EXTENDED_YEAR. Two options: 1. Make sure resource data is
0082: // correct; 2. Make code add in 'u' at end if len == 17.
0083:
0084: // Constants for context
0085: /**
0086: * Constant for context.
0087: * @draft ICU 3.6
0088: * @provisional This API might change or be removed in a future release.
0089: */
0090: public static final int FORMAT = 0;
0091:
0092: /**
0093: * Constant for context.
0094: * @draft ICU 3.6
0095: * @provisional This API might change or be removed in a future release.
0096: */
0097: public static final int STANDALONE = 1;
0098:
0099: /**
0100: * Constant for context.
0101: * @internal revisit for ICU 3.6
0102: * @deprecated This API is ICU internal only.
0103: */
0104: public static final int DT_CONTEXT_COUNT = 2;
0105:
0106: // Constants for width
0107:
0108: /**
0109: * Constant for width.
0110: * @draft ICU 3.6
0111: * @provisional This API might change or be removed in a future release.
0112: */
0113: public static final int ABBREVIATED = 0;
0114:
0115: /**
0116: * Constant for width.
0117: * @draft ICU 3.6
0118: * @provisional This API might change or be removed in a future release.
0119: */
0120: public static final int WIDE = 1;
0121:
0122: /**
0123: * Constant for width.
0124: * @draft ICU 3.6
0125: * @provisional This API might change or be removed in a future release.
0126: */
0127: public static final int NARROW = 2;
0128:
0129: /**
0130: * Constant for width.
0131: * @internal revisit for ICU 3.6
0132: * @deprecated This API is ICU internal only.
0133: */
0134: public static final int DT_WIDTH_COUNT = 3;
0135:
0136: /**
0137: * Construct a DateFormatSymbols object by loading format data from
0138: * resources for the default locale.
0139: *
0140: * @throws java.util.MissingResourceException
0141: * if the resources for the default locale cannot be
0142: * found or cannot be loaded.
0143: * @stable ICU 2.0
0144: */
0145: public DateFormatSymbols() {
0146: initializeData(ULocale.getDefault(), ""); // TODO: type?
0147: }
0148:
0149: /**
0150: * Construct a DateFormatSymbols object by loading format data from
0151: * resources for the given locale.
0152: *
0153: * @throws java.util.MissingResourceException
0154: * if the resources for the specified locale cannot be
0155: * found or cannot be loaded.
0156: * @stable ICU 2.0
0157: */
0158: public DateFormatSymbols(Locale locale) {
0159: initializeData(ULocale.forLocale(locale), ""); // TODO: type?
0160: }
0161:
0162: /**
0163: * Construct a DateFormatSymbols object by loading format data from
0164: * resources for the given ulocale.
0165: *
0166: * @throws java.util.MissingResourceException
0167: * if the resources for the specified locale cannot be
0168: * found or cannot be loaded.
0169: * @draft ICU 3.2
0170: * @provisional This API might change or be removed in a future release.
0171: */
0172: public DateFormatSymbols(ULocale locale) {
0173: initializeData(locale, ""); // TODO: type?
0174: }
0175:
0176: /**
0177: * Era strings. For example: "AD" and "BC". An array of 2 strings,
0178: * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
0179: * @serial
0180: */
0181: String eras[] = null;
0182:
0183: /**
0184: * Era name strings. For example: "Anno Domini" and "Before Christ". An array of 2 strings,
0185: * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
0186: * @serial
0187: */
0188: String eraNames[] = null;
0189:
0190: /**
0191: * Narrow era names. For example: "A" and "B". An array of 2 strings,
0192: * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
0193: * @serial
0194: */
0195: String narrowEras[] = null;
0196:
0197: /**
0198: * Month strings. For example: "January", "February", etc. An array
0199: * of 13 strings (some calendars have 13 months), indexed by
0200: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0201: * @serial
0202: */
0203: String months[] = null;
0204:
0205: /**
0206: * Short month strings. For example: "Jan", "Feb", etc. An array of
0207: * 13 strings (some calendars have 13 months), indexed by
0208: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0209:
0210: * @serial
0211: */
0212: String shortMonths[] = null;
0213:
0214: /**
0215: * Narrow month strings. For example: "J", "F", etc. An array of
0216: * 13 strings (some calendars have 13 months), indexed by
0217: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0218:
0219: * @serial
0220: */
0221: String narrowMonths[] = null;
0222:
0223: /**
0224: * Standalone month strings. For example: "January", "February", etc. An array
0225: * of 13 strings (some calendars have 13 months), indexed by
0226: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0227: * @serial
0228: */
0229: String standaloneMonths[] = null;
0230:
0231: /**
0232: * Standalone short month strings. For example: "Jan", "Feb", etc. An array of
0233: * 13 strings (some calendars have 13 months), indexed by
0234: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0235:
0236: * @serial
0237: */
0238: String standaloneShortMonths[] = null;
0239:
0240: /**
0241: * Standalone narrow month strings. For example: "J", "F", etc. An array of
0242: * 13 strings (some calendars have 13 months), indexed by
0243: * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
0244:
0245: * @serial
0246: */
0247: String standaloneNarrowMonths[] = null;
0248:
0249: /**
0250: * Weekday strings. For example: "Sunday", "Monday", etc. An array
0251: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0252: * <code>Calendar.MONDAY</code>, etc.
0253: * The element <code>weekdays[0]</code> is ignored.
0254: * @serial
0255: */
0256: String weekdays[] = null;
0257:
0258: /**
0259: * Short weekday strings. For example: "Sun", "Mon", etc. An array
0260: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0261: * <code>Calendar.MONDAY</code>, etc.
0262: * The element <code>shortWeekdays[0]</code> is ignored.
0263: * @serial
0264: */
0265: String shortWeekdays[] = null;
0266:
0267: /**
0268: * Narrow weekday strings. For example: "S", "M", etc. An array
0269: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0270: * <code>Calendar.MONDAY</code>, etc.
0271: * The element <code>narrowWeekdays[0]</code> is ignored.
0272: * @serial
0273: */
0274: String narrowWeekdays[] = null;
0275:
0276: /**
0277: * Standalone weekday strings. For example: "Sunday", "Monday", etc. An array
0278: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0279: * <code>Calendar.MONDAY</code>, etc.
0280: * The element <code>standaloneWeekdays[0]</code> is ignored.
0281: * @serial
0282: */
0283: String standaloneWeekdays[] = null;
0284:
0285: /**
0286: * Standalone short weekday strings. For example: "Sun", "Mon", etc. An array
0287: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0288: * <code>Calendar.MONDAY</code>, etc.
0289: * The element <code>standaloneShortWeekdays[0]</code> is ignored.
0290: * @serial
0291: */
0292: String standaloneShortWeekdays[] = null;
0293:
0294: /**
0295: * Standalone narrow weekday strings. For example: "S", "M", etc. An array
0296: * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
0297: * <code>Calendar.MONDAY</code>, etc.
0298: * The element <code>standaloneNarrowWeekdays[0]</code> is ignored.
0299: * @serial
0300: */
0301: String standaloneNarrowWeekdays[] = null;
0302:
0303: /**
0304: * AM and PM strings. For example: "AM" and "PM". An array of
0305: * 2 strings, indexed by <code>Calendar.AM</code> and
0306: * <code>Calendar.PM</code>.
0307: * @serial
0308: */
0309: String ampms[] = null;
0310:
0311: /**
0312: * Abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array
0313: * of 4 strings indexed by the month divided by 3.
0314: * @serial
0315: */
0316: String shortQuarters[] = null;
0317:
0318: /**
0319: * Full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter",
0320: * "4th Quarter". An array of 4 strings, indexed by the month divided by 3.
0321: * @serial
0322: */
0323: String quarters[] = null;
0324:
0325: /**
0326: * Standalone abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array
0327: * of 4 strings indexed by the month divided by 3.
0328: * @serial
0329: */
0330: String standaloneShortQuarters[] = null;
0331:
0332: /**
0333: * Standalone full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter",
0334: * "4th Quarter". An array of 4 strings, indexed by the month divided by 3.
0335: * @serial
0336: */
0337: String standaloneQuarters[] = null;
0338:
0339: /**
0340: * Localized names of time zones in this locale. This is a
0341: * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
0342: * where <em>m</em> is at least 5 and up to 8. Each of the <em>n</em> rows is an
0343: * entry containing the localized names for a single <code>TimeZone</code>.
0344: * Each such row contains (with <code>i</code> ranging from
0345: * 0..<em>n</em>-1):
0346: * <ul>
0347: * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
0348: * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
0349: * time</li>
0350: * <li><code>zoneStrings[i][2]</code> - short name of zone in
0351: * standard time</li>
0352: * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
0353: * savings time</li>
0354: * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
0355: * savings time</li>
0356: * <li>The remainder varies depending on whether there is data
0357: * on city name or generic time. The city name, if available, comes
0358: * first. The long and short generic times, if available, come next,
0359: * in that order. The length of the array (m) can be examined to
0360: * determine which optional information is available.</li>
0361: * </ul>
0362: * The zone ID is <em>not</em> localized; it corresponds to the ID
0363: * value associated with a system time zone object. All other entries
0364: * are localized names. If a zone does not implement daylight savings
0365: * time, the daylight savings time names are ignored.
0366: * @see com.ibm.icu.util.TimeZone
0367: * @serial
0368: */
0369: private String zoneStrings[][] = null;
0370:
0371: /**
0372: * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
0373: * All locales use the same unlocalized pattern characters.
0374: */
0375: static final String patternChars = "GyMdkHmsSEDFwWahKzYeugAZvcLQq";
0376:
0377: /**
0378: * Localized date-time pattern characters. For example, a locale may
0379: * wish to use 'u' rather than 'y' to represent years in its date format
0380: * pattern strings.
0381: * This string must be exactly 18 characters long, with the index of
0382: * the characters described by <code>DateFormat.ERA_FIELD</code>,
0383: * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were
0384: * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
0385: * @serial
0386: */
0387: String localPatternChars = null;
0388:
0389: /* use serialVersionUID from JDK 1.1.4 for interoperability */
0390: private static final long serialVersionUID = -5987973545549424702L;
0391:
0392: /**
0393: * Gets era strings. For example: "AD" and "BC".
0394: * @return the era strings.
0395: * @stable ICU 2.0
0396: */
0397: public String[] getEras() {
0398: return duplicate(eras);
0399: }
0400:
0401: /**
0402: * Sets era strings. For example: "AD" and "BC".
0403: * @param newEras the new era strings.
0404: * @stable ICU 2.0
0405: */
0406: public void setEras(String[] newEras) {
0407: eras = duplicate(newEras);
0408: }
0409:
0410: /**
0411: * Gets era name strings. For example: "Anno Domini" and "Before Christ".
0412: * @return the era strings.
0413: * @draft ICU 3.4
0414: * @provisional This API might change or be removed in a future release.
0415: */
0416: public String[] getEraNames() {
0417: return duplicate(eraNames);
0418: }
0419:
0420: /**
0421: * Sets era name strings. For example: "Anno Domini" and "Before Christ".
0422: * @param newEraNames the new era strings.
0423: * @internal revisit for ICU 3.6
0424: * @deprecated This API is ICU internal only.
0425: */
0426: public void setEraNames(String[] newEraNames) {
0427: eraNames = duplicate(newEraNames);
0428: }
0429:
0430: /**
0431: * Gets month strings. For example: "January", "February", etc.
0432: * @return the month strings.
0433: * @stable ICU 2.0
0434: */
0435: public String[] getMonths() {
0436: return duplicate(months);
0437: }
0438:
0439: /**
0440: * Gets month strings. For example: "January", "February", etc.
0441: * @param context The month context, FORMAT or STANDALONE.
0442: * @param width The width or the returned month string,
0443: * either WIDE, ABBREVIATED, or NARROW.
0444: * @return the month strings.
0445: * @draft ICU 3.4
0446: * @provisional This API might change or be removed in a future release.
0447: */
0448: public String[] getMonths(int context, int width) {
0449: String[] returnValue = null;
0450: switch (context) {
0451: case FORMAT:
0452: switch (width) {
0453: case WIDE:
0454: returnValue = months;
0455: break;
0456: case ABBREVIATED:
0457: returnValue = shortMonths;
0458: break;
0459: case NARROW:
0460: returnValue = narrowMonths;
0461: break;
0462: }
0463: break;
0464: case STANDALONE:
0465: switch (width) {
0466: case WIDE:
0467: returnValue = standaloneMonths;
0468: break;
0469: case ABBREVIATED:
0470: returnValue = standaloneShortMonths;
0471: break;
0472: case NARROW:
0473: returnValue = standaloneNarrowMonths;
0474: break;
0475: }
0476: break;
0477: }
0478: return duplicate(returnValue);
0479: }
0480:
0481: /**
0482: * Sets month strings. For example: "January", "February", etc.
0483: * @param newMonths the new month strings.
0484: * @stable ICU 2.0
0485: */
0486: public void setMonths(String[] newMonths) {
0487: months = duplicate(newMonths);
0488: }
0489:
0490: /**
0491: * Sets month strings. For example: "January", "February", etc.
0492: * @param newMonths the new month strings.
0493: * @param context The formatting context, FORMAT or STANDALONE.
0494: * @param width The width of the month string,
0495: * either WIDE, ABBREVIATED, or NARROW.
0496: * @internal revisit for ICU 3.6
0497: * @deprecated This API is ICU internal only.
0498: */
0499: public void setMonths(String[] newMonths, int context, int width) {
0500: switch (context) {
0501: case FORMAT:
0502: switch (width) {
0503: case WIDE:
0504: months = duplicate(newMonths);
0505: break;
0506: case ABBREVIATED:
0507: shortMonths = duplicate(newMonths);
0508: break;
0509: case NARROW:
0510: narrowMonths = duplicate(newMonths);
0511: break;
0512: }
0513: break;
0514: case STANDALONE:
0515: switch (width) {
0516: case WIDE:
0517: standaloneMonths = duplicate(newMonths);
0518: break;
0519: case ABBREVIATED:
0520: standaloneShortMonths = duplicate(newMonths);
0521: break;
0522: case NARROW:
0523: standaloneNarrowMonths = duplicate(newMonths);
0524: break;
0525: }
0526: break;
0527: }
0528: }
0529:
0530: /**
0531: * Gets short month strings. For example: "Jan", "Feb", etc.
0532: * @return the short month strings.
0533: * @stable ICU 2.0
0534: */
0535: public String[] getShortMonths() {
0536: return duplicate(shortMonths);
0537: }
0538:
0539: /**
0540: * Sets short month strings. For example: "Jan", "Feb", etc.
0541: * @param newShortMonths the new short month strings.
0542: * @stable ICU 2.0
0543: */
0544: public void setShortMonths(String[] newShortMonths) {
0545: shortMonths = duplicate(newShortMonths);
0546: }
0547:
0548: /**
0549: * Gets weekday strings. For example: "Sunday", "Monday", etc.
0550: * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
0551: * <code>Calendar.MONDAY</code>, etc. to index the result array.
0552: * @stable ICU 2.0
0553: */
0554: public String[] getWeekdays() {
0555: return duplicate(weekdays);
0556: }
0557:
0558: /**
0559: * Gets weekday strings. For example: "Sunday", "Monday", etc.
0560: * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
0561: * <code>Calendar.MONDAY</code>, etc. to index the result array.
0562: * @param context Formatting context, either FORMAT or STANDALONE.
0563: * @param width Width of strings to be returned, either
0564: * WIDE, ABBREVIATED, or NARROW
0565: * @draft ICU 3.4
0566: * @provisional This API might change or be removed in a future release.
0567: */
0568: public String[] getWeekdays(int context, int width) {
0569: String[] returnValue = null;
0570: switch (context) {
0571: case FORMAT:
0572: switch (width) {
0573: case WIDE:
0574: returnValue = weekdays;
0575: break;
0576: case ABBREVIATED:
0577: returnValue = shortWeekdays;
0578: break;
0579: case NARROW:
0580: returnValue = narrowWeekdays;
0581: break;
0582: }
0583: break;
0584: case STANDALONE:
0585: switch (width) {
0586: case WIDE:
0587: returnValue = standaloneWeekdays;
0588: break;
0589: case ABBREVIATED:
0590: returnValue = standaloneShortWeekdays;
0591: break;
0592: case NARROW:
0593: returnValue = standaloneNarrowWeekdays;
0594: break;
0595: }
0596: break;
0597: }
0598: return duplicate(returnValue);
0599: }
0600:
0601: /**
0602: * Sets weekday strings. For example: "Sunday", "Monday", etc.
0603: * @param newWeekdays The new weekday strings.
0604: * @param context The formatting context, FORMAT or STANDALONE.
0605: * @param width The width of the strings,
0606: * either WIDE, ABBREVIATED, or NARROW.
0607: * @internal revisit for ICU 3.6
0608: * @deprecated This API is ICU internal only.
0609: */
0610: public void setWeekdays(String[] newWeekdays, int context, int width) {
0611: switch (context) {
0612: case FORMAT:
0613: switch (width) {
0614: case WIDE:
0615: weekdays = duplicate(newWeekdays);
0616: break;
0617: case ABBREVIATED:
0618: shortWeekdays = duplicate(newWeekdays);
0619: break;
0620: case NARROW:
0621: narrowWeekdays = duplicate(newWeekdays);
0622: break;
0623: }
0624: break;
0625: case STANDALONE:
0626: switch (width) {
0627: case WIDE:
0628: standaloneWeekdays = duplicate(newWeekdays);
0629: break;
0630: case ABBREVIATED:
0631: standaloneShortWeekdays = duplicate(newWeekdays);
0632: break;
0633: case NARROW:
0634: standaloneNarrowWeekdays = duplicate(newWeekdays);
0635: break;
0636: }
0637: break;
0638: }
0639: }
0640:
0641: /**
0642: * Sets weekday strings. For example: "Sunday", "Monday", etc.
0643: * @param newWeekdays the new weekday strings. The array should
0644: * be indexed by <code>Calendar.SUNDAY</code>,
0645: * <code>Calendar.MONDAY</code>, etc.
0646: * @stable ICU 2.0
0647: */
0648: public void setWeekdays(String[] newWeekdays) {
0649: weekdays = duplicate(newWeekdays);
0650: }
0651:
0652: /**
0653: * Gets short weekday strings. For example: "Sun", "Mon", etc.
0654: * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
0655: * <code>Calendar.MONDAY</code>, etc. to index the result array.
0656: * @stable ICU 2.0
0657: */
0658: public String[] getShortWeekdays() {
0659: return duplicate(shortWeekdays);
0660: }
0661:
0662: /**
0663: * Sets short weekday strings. For example: "Sun", "Mon", etc.
0664: * @param newShortWeekdays the new short weekday strings. The array should
0665: * be indexed by <code>Calendar.SUNDAY</code>,
0666: * <code>Calendar.MONDAY</code>, etc.
0667: * @stable ICU 2.0
0668: */
0669: public void setShortWeekdays(String[] newShortWeekdays) {
0670: shortWeekdays = duplicate(newShortWeekdays);
0671: }
0672:
0673: /**
0674: * Gets quarter strings. For example: "1st Quarter", "2nd Quarter", etc.
0675: * @param context The quarter context, FORMAT or STANDALONE.
0676: * @param width The width or the returned quarter string,
0677: * either WIDE or ABBREVIATED. There are no NARROW quarters.
0678: * @return the quarter strings.
0679: * @draft ICU 3.6
0680: * @provisional This API might change or be removed in a future release.
0681: */
0682: public String[] getQuarters(int context, int width) {
0683: String[] returnValue = null;
0684: switch (context) {
0685: case FORMAT:
0686: switch (width) {
0687: case WIDE:
0688: returnValue = quarters;
0689: break;
0690: case ABBREVIATED:
0691: returnValue = shortQuarters;
0692: break;
0693: case NARROW:
0694: returnValue = null;
0695: break;
0696: }
0697: break;
0698:
0699: case STANDALONE:
0700: switch (width) {
0701: case WIDE:
0702: returnValue = standaloneQuarters;
0703: break;
0704: case ABBREVIATED:
0705: returnValue = standaloneShortQuarters;
0706: break;
0707: case NARROW:
0708: returnValue = null;
0709: break;
0710: }
0711: break;
0712: }
0713: return duplicate(returnValue);
0714: }
0715:
0716: /**
0717: * Sets quarter strings. For example: "1st Quarter", "2nd Quarter", etc.
0718: * @param newQuarters the new quarter strings.
0719: * @param context The formatting context, FORMAT or STANDALONE.
0720: * @param width The width of the quarter string,
0721: * either WIDE or ABBREVIATED. There are no NARROW quarters.
0722: * @internal revisit for ICU 3.6
0723: * @deprecated This API is ICU internal only.
0724: */
0725: public void setQuarters(String[] newQuarters, int context, int width) {
0726: switch (context) {
0727: case FORMAT:
0728: switch (width) {
0729: case WIDE:
0730: quarters = duplicate(newQuarters);
0731: break;
0732: case ABBREVIATED:
0733: shortQuarters = duplicate(newQuarters);
0734: break;
0735: case NARROW:
0736: //narrowQuarters = duplicate(newQuarters);
0737: break;
0738: }
0739: break;
0740: case STANDALONE:
0741: switch (width) {
0742: case WIDE:
0743: standaloneQuarters = duplicate(newQuarters);
0744: break;
0745: case ABBREVIATED:
0746: standaloneShortQuarters = duplicate(newQuarters);
0747: break;
0748: case NARROW:
0749: //standaloneNarrowQuarters = duplicate(newQuarters);
0750: break;
0751: }
0752: break;
0753: }
0754: }
0755:
0756: /**
0757: * Gets ampm strings. For example: "AM" and "PM".
0758: * @return the weekday strings.
0759: * @stable ICU 2.0
0760: */
0761: public String[] getAmPmStrings() {
0762: return duplicate(ampms);
0763: }
0764:
0765: /**
0766: * Sets ampm strings. For example: "AM" and "PM".
0767: * @param newAmpms the new ampm strings.
0768: * @stable ICU 2.0
0769: */
0770: public void setAmPmStrings(String[] newAmpms) {
0771: ampms = duplicate(newAmpms);
0772: }
0773:
0774: /**
0775: * Gets timezone strings.
0776: * @return the timezone strings.
0777: * @stable ICU 2.0
0778: */
0779: public String[][] getZoneStrings() {
0780: String[][] strings = zoneStrings;
0781: if (strings == null) {
0782: // get the default zone strings
0783: ZoneItemInfo zii = getDefaultZoneItemInfo();
0784: strings = zii.tzStrings;
0785: }
0786: return duplicate(strings);
0787: }
0788:
0789: /**
0790: * Sets timezone strings.
0791: * @param newZoneStrings the new timezone strings.
0792: * @stable ICU 2.0
0793: */
0794: public void setZoneStrings(String[][] newZoneStrings) {
0795: zoneStrings = duplicate(newZoneStrings);
0796: // need to update local zone item info
0797: localZoneItemInfo = null;
0798: }
0799:
0800: /**
0801: * Gets localized date-time pattern characters. For example: 'u', 't', etc.
0802: * @return the localized date-time pattern characters.
0803: * @stable ICU 2.0
0804: */
0805: public String getLocalPatternChars() {
0806: return new String(localPatternChars);
0807: }
0808:
0809: /**
0810: * Sets localized date-time pattern characters. For example: 'u', 't', etc.
0811: * @param newLocalPatternChars the new localized date-time
0812: * pattern characters.
0813: * @stable ICU 2.0
0814: */
0815: public void setLocalPatternChars(String newLocalPatternChars) {
0816: localPatternChars = newLocalPatternChars;
0817: }
0818:
0819: /**
0820: * Overrides Cloneable
0821: * @stable ICU 2.0
0822: */
0823: public Object clone() {
0824: try {
0825: DateFormatSymbols other = (DateFormatSymbols) super .clone();
0826: copyMembers(this , other);
0827: return other;
0828: } catch (CloneNotSupportedException e) {
0829: ///CLOVER:OFF
0830: throw new IllegalStateException();
0831: ///CLOVER:ON
0832: }
0833: }
0834:
0835: /**
0836: * Override hashCode.
0837: * Generates a hash code for the DateFormatSymbols object.
0838: * @stable ICU 2.0
0839: */
0840: public int hashCode() {
0841: int hashcode = 0;
0842: hashcode ^= requestedLocale.toString().hashCode();
0843: String[][] tzStrings = zoneStrings;
0844: if (tzStrings == null) {
0845: ZoneItemInfo zii = getDefaultZoneItemInfo();
0846: tzStrings = zii.tzStrings;
0847: }
0848: for (int i = 0; i < tzStrings.length; i++) {
0849: for (int j = 0; j < tzStrings[i].length; j++) {
0850: if (tzStrings[i][j] != null) {
0851: hashcode ^= tzStrings[i][j].hashCode();
0852: }
0853: }
0854: }
0855: return hashcode;
0856: }
0857:
0858: /**
0859: * Override equals
0860: * @stable ICU 2.0
0861: */
0862: public boolean equals(Object obj) {
0863: if (this == obj)
0864: return true;
0865: if (obj == null || getClass() != obj.getClass())
0866: return false;
0867: DateFormatSymbols that = (DateFormatSymbols) obj;
0868: return (Utility.arrayEquals(eras, that.eras)
0869: && Utility.arrayEquals(eraNames, that.eraNames)
0870: && Utility.arrayEquals(months, that.months)
0871: && Utility.arrayEquals(shortMonths, that.shortMonths)
0872: && Utility.arrayEquals(narrowMonths, that.narrowMonths)
0873: && Utility.arrayEquals(standaloneMonths,
0874: that.standaloneMonths)
0875: && Utility.arrayEquals(standaloneShortMonths,
0876: that.standaloneShortMonths)
0877: && Utility.arrayEquals(standaloneNarrowMonths,
0878: that.standaloneNarrowMonths)
0879: && Utility.arrayEquals(weekdays, that.weekdays)
0880: && Utility.arrayEquals(shortWeekdays,
0881: that.shortWeekdays)
0882: && Utility.arrayEquals(narrowWeekdays,
0883: that.narrowWeekdays)
0884: && Utility.arrayEquals(standaloneWeekdays,
0885: that.standaloneWeekdays)
0886: && Utility.arrayEquals(standaloneShortWeekdays,
0887: that.standaloneShortWeekdays)
0888: && Utility.arrayEquals(standaloneNarrowWeekdays,
0889: that.standaloneNarrowWeekdays)
0890: && Utility.arrayEquals(ampms, that.ampms)
0891: && arrayOfArrayEquals(zoneStrings, that.zoneStrings)
0892: // getDiplayName maps deprecated country and language codes to the current ones
0893: // too bad there is no way to get the current codes!
0894: // I thought canolicalize() would map the codes but .. alas! it doesn't.
0895: && requestedLocale.getDisplayName().equals(
0896: that.requestedLocale.getDisplayName()) && Utility
0897: .arrayEquals(localPatternChars, that.localPatternChars));
0898: }
0899:
0900: // =======================privates===============================
0901:
0902: /**
0903: * Useful constant for defining timezone offsets.
0904: */
0905: static final int millisPerHour = 60 * 60 * 1000;
0906:
0907: /**
0908: *
0909: * @param desiredLocale
0910: * @param type
0911: * @stable ICU 3.0
0912: */
0913: protected void initializeData(ULocale desiredLocale, String type) {
0914: CalendarData calData = new CalendarData(desiredLocale, type);
0915: initializeData(desiredLocale, calData);
0916: }
0917:
0918: /**
0919: *
0920: * @param desiredLocale
0921: * @param calData
0922: * @stable ICU 3.0
0923: */
0924: protected void initializeData(ULocale desiredLocale,
0925: CalendarData calData) {
0926:
0927: // FIXME: cache only ResourceBundle. Hence every time, will do
0928: // getObject(). This won't be necessary if the Resource itself
0929: // is cached.
0930: eras = calData.getEras("abbreviated");
0931:
0932: try {
0933: eraNames = calData.getEras("wide");
0934: } catch (MissingResourceException e) {
0935: eraNames = calData.getEras("abbreviated");
0936: }
0937:
0938: // NOTE: since the above code assumes that abbreviated
0939: // era names exist, we make the same assumption here too.
0940: try {
0941: narrowEras = calData.getEras("narrow");
0942: } catch (MissingResourceException e) {
0943: narrowEras = calData.getEras("abbreviated");
0944: }
0945:
0946: months = calData.getStringArray("monthNames", "wide");
0947: shortMonths = calData.getStringArray("monthNames",
0948: "abbreviated");
0949:
0950: try {
0951: narrowMonths = calData.getStringArray("monthNames",
0952: "narrow");
0953: } catch (MissingResourceException e) {
0954: try {
0955: narrowMonths = calData.getStringArray("monthNames",
0956: "stand-alone", "narrow");
0957: } catch (MissingResourceException e1) {
0958: narrowMonths = calData.getStringArray("monthNames",
0959: "abbreviated");
0960: }
0961: }
0962:
0963: try {
0964: standaloneMonths = calData.getStringArray("monthNames",
0965: "stand-alone", "wide");
0966: } catch (MissingResourceException e) {
0967: standaloneMonths = calData.getStringArray("monthNames",
0968: "format", "wide");
0969: }
0970:
0971: try {
0972: standaloneShortMonths = calData.getStringArray(
0973: "monthNames", "stand-alone", "abbreviated");
0974: } catch (MissingResourceException e) {
0975: standaloneShortMonths = calData.getStringArray(
0976: "monthNames", "format", "abbreviated");
0977: }
0978:
0979: try {
0980: standaloneNarrowMonths = calData.getStringArray(
0981: "monthNames", "stand-alone", "narrow");
0982: } catch (MissingResourceException e) {
0983: try {
0984: standaloneNarrowMonths = calData.getStringArray(
0985: "monthNames", "format", "narrow");
0986: } catch (MissingResourceException e1) {
0987: standaloneNarrowMonths = calData.getStringArray(
0988: "monthNames", "format", "abbreviated");
0989: }
0990: }
0991:
0992: String[] lWeekdays = calData.getStringArray("dayNames", "wide");
0993: weekdays = new String[8];
0994: weekdays[0] = ""; // 1-based
0995: System.arraycopy(lWeekdays, 0, weekdays, 1, lWeekdays.length);
0996:
0997: String[] sWeekdays = calData.getStringArray("dayNames",
0998: "abbreviated");
0999: shortWeekdays = new String[8];
1000: shortWeekdays[0] = ""; // 1-based
1001: System.arraycopy(sWeekdays, 0, shortWeekdays, 1,
1002: sWeekdays.length);
1003:
1004: String[] nWeekdays = null;
1005: try {
1006: nWeekdays = calData.getStringArray("dayNames", "narrow");
1007: } catch (MissingResourceException e) {
1008: try {
1009: nWeekdays = calData.getStringArray("dayNames",
1010: "stand-alone", "narrow");
1011: } catch (MissingResourceException e1) {
1012: nWeekdays = calData.getStringArray("dayNames",
1013: "abbreviated");
1014: }
1015: }
1016: narrowWeekdays = new String[8];
1017: narrowWeekdays[0] = ""; // 1-based
1018: System.arraycopy(nWeekdays, 0, narrowWeekdays, 1,
1019: nWeekdays.length);
1020:
1021: String[] saWeekdays = null;
1022: try {
1023: saWeekdays = calData.getStringArray("dayNames",
1024: "stand-alone", "wide");
1025: } catch (MissingResourceException e) {
1026: saWeekdays = calData.getStringArray("dayNames", "format",
1027: "wide");
1028: }
1029: standaloneWeekdays = new String[8];
1030: standaloneWeekdays[0] = ""; // 1-based
1031: System.arraycopy(saWeekdays, 0, standaloneWeekdays, 1,
1032: saWeekdays.length);
1033:
1034: String[] ssWeekdays = null;
1035: try {
1036: ssWeekdays = calData.getStringArray("dayNames",
1037: "stand-alone", "abbreviated");
1038: } catch (MissingResourceException e) {
1039: ssWeekdays = calData.getStringArray("dayNames", "format",
1040: "abbreviated");
1041: }
1042: standaloneShortWeekdays = new String[8];
1043: standaloneShortWeekdays[0] = ""; // 1-based
1044: System.arraycopy(ssWeekdays, 0, standaloneShortWeekdays, 1,
1045: ssWeekdays.length);
1046:
1047: String[] snWeekdays = null;
1048: try {
1049: snWeekdays = calData.getStringArray("dayNames",
1050: "stand-alone", "narrow");
1051: } catch (MissingResourceException e) {
1052: try {
1053: snWeekdays = calData.getStringArray("dayNames",
1054: "format", "narrow");
1055: } catch (MissingResourceException e1) {
1056: snWeekdays = calData.getStringArray("dayNames",
1057: "format", "abbreviated");
1058: }
1059: }
1060: standaloneNarrowWeekdays = new String[8];
1061: standaloneNarrowWeekdays[0] = ""; // 1-based
1062: System.arraycopy(snWeekdays, 0, standaloneNarrowWeekdays, 1,
1063: snWeekdays.length);
1064:
1065: ampms = calData.getStringArray("AmPmMarkers");
1066:
1067: quarters = calData.getStringArray("quarters", "wide");
1068: shortQuarters = calData.getStringArray("quarters",
1069: "abbreviated");
1070:
1071: try {
1072: standaloneQuarters = calData.getStringArray("quarters",
1073: "stand-alone", "wide");
1074: } catch (MissingResourceException e) {
1075: standaloneQuarters = calData.getStringArray("quarters",
1076: "format", "wide");
1077: }
1078:
1079: try {
1080: standaloneShortQuarters = calData.getStringArray(
1081: "quarters", "stand-alone", "abbreviated");
1082: } catch (MissingResourceException e) {
1083: standaloneShortQuarters = calData.getStringArray(
1084: "quarters", "format", "abbreviated");
1085: }
1086:
1087: /* THE FOLLOWING DOESN'T WORK; A COUNTRY LOCALE WITH ONE ZONE BLOCKS THE LANGUAGE LOCALE
1088: // These really do use rb and not calData
1089: ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, desiredLocale);
1090: // hack around class cast problem
1091: // zoneStrings = (String[][])rb.getObject("zoneStrings");
1092: ICUResourceBundle zoneObject = rb.get("zoneStrings");
1093: zoneStrings = new String[zoneObject.getSize()][];
1094: for(int i =0; i< zoneObject.getSize(); i++){
1095: ICUResourceBundle zoneArr = zoneObject.get(i);
1096: String[] strings = new String[zoneArr.getSize()];
1097: for(int j=0; j<zoneArr.getSize(); j++){
1098: strings[j]=zoneArr.get(j).getString();
1099: }
1100: zoneStrings[i] = strings;
1101: }
1102: */
1103: requestedLocale = desiredLocale;
1104:
1105: ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle
1106: .getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
1107: desiredLocale);
1108: localPatternChars = rb.getString("localPatternChars");
1109:
1110: // TODO: obtain correct actual/valid locale later
1111: ULocale uloc = rb.getULocale();
1112: setLocale(uloc, uloc);
1113: }
1114:
1115: private static final boolean arrayOfArrayEquals(Object[][] aa1,
1116: Object[][] aa2) {
1117: if (aa1 == aa2) { // both are null
1118: return true;
1119: }
1120: if (aa1 == null || aa2 == null) { // one is null and the other is not
1121: return false;
1122: }
1123: if (aa1.length != aa2.length) {
1124: return false;
1125: }
1126: boolean equal = true;
1127: for (int i = 0; i < aa1.length; i++) {
1128: equal = Utility.arrayEquals(aa1[i], aa2[i]);
1129: if (!equal) {
1130: break;
1131: }
1132: }
1133: return equal;
1134: }
1135:
1136: /**
1137: * Package private: used by SimpleDateFormat.
1138: * Gets the string for the specified time zone.
1139: * @param zid The time zone ID
1140: * @param type The type of zone string
1141: * @return The zone string, or null if not available.
1142: */
1143: String getZoneString(String zid, int type) {
1144: // Try local zone item info first
1145: String zoneString = getZoneString(getLocalZoneItemInfo(), zid,
1146: type);
1147: if (zoneString == null) {
1148: // Fallback to the default info
1149: zoneString = getZoneString(getDefaultZoneItemInfo(), zid,
1150: type);
1151: }
1152: return zoneString;
1153: }
1154:
1155: /**
1156: * Gets the zone string from the specified zone item info
1157: */
1158: private String getZoneString(ZoneItemInfo zinfo, String zid,
1159: int type) {
1160: if (zinfo == null) {
1161: return null;
1162: }
1163: String[] names = (String[]) zinfo.tzidMap.get(zid);
1164: if (names != null) {
1165: // get name for the type
1166: int index = -1;
1167: switch (type) {
1168: case TIMEZONE_LONG_STANDARD:
1169: index = 1;
1170: break;
1171: case TIMEZONE_SHORT_STANDARD:
1172: index = 2;
1173: break;
1174: case TIMEZONE_LONG_DAYLIGHT:
1175: index = 3;
1176: break;
1177: case TIMEZONE_SHORT_DAYLIGHT:
1178: index = 4;
1179: break;
1180: case TIMEZONE_EXEMPLAR_CITY:
1181: if (names.length == 6 || names.length == 8) {
1182: index = 5;
1183: }
1184: break;
1185: case TIMEZONE_LONG_GENERIC:
1186: if (names.length == 8) {
1187: index = 6;
1188: } else {
1189: index = 5;
1190: }
1191: break;
1192: case TIMEZONE_SHORT_GENERIC:
1193: if (names.length == 8) {
1194: index = 7;
1195: } else {
1196: index = 6;
1197: }
1198: }
1199: if (index < names.length) {
1200: return names[index];
1201: }
1202: }
1203: return null;
1204: }
1205:
1206: class ZoneItem {
1207: String value;
1208: int type;
1209: String zid;
1210: }
1211:
1212: /**
1213: * Package private: used by SimpleDateformat
1214: * Gets the ZoneItem instance which has zone strings
1215: * which matches the specified text.
1216: * @param text The text which contains a zone string
1217: * @param start The start position of zone string in the text
1218: * @return A ZonItem instance for the longest matching zone
1219: * string.
1220: */
1221: ZoneItem findZoneIDTypeValue(String text, int start) {
1222: ZoneItem item = null;
1223: int textLength = text.length() - start;
1224: if (lastZoneItem != null
1225: && textLength == lastZoneItem.value.length()) {
1226: if (text.regionMatches(true, start, lastZoneItem.value, 0,
1227: textLength)) {
1228: item = new ZoneItem();
1229: item.type = lastZoneItem.type;
1230: item.value = lastZoneItem.value;
1231: item.zid = lastZoneItem.zid;
1232: return item;
1233: }
1234: }
1235:
1236: ZoneItemInfo zinfo = getLocalZoneItemInfo();
1237: if (zinfo != null) {
1238: // look up the zone string in localZoneItemInfo first
1239: item = (ZoneItem) zinfo.tzStringMap.get(text, start);
1240: }
1241:
1242: // look up the zone string in default ZoneItemInfo for the locale
1243: zinfo = getDefaultZoneItemInfo();
1244: ZoneItem itemForLocale = (ZoneItem) zinfo.tzStringMap.get(text,
1245: start);
1246: if (itemForLocale != null) {
1247: // we want to use longer match
1248: if (item == null
1249: || itemForLocale.value.length() > item.value
1250: .length()) {
1251: item = itemForLocale;
1252: }
1253: }
1254:
1255: if (item != null && textLength == item.value.length()) {
1256: // clone the last match for next time
1257: // only when the substring completely matches
1258: // with the value resolved
1259: lastZoneItem = new ZoneItem();
1260: lastZoneItem.type = item.type;
1261: lastZoneItem.value = item.value;
1262: lastZoneItem.zid = item.zid;
1263: }
1264: return item;
1265: }
1266:
1267: /**
1268: * A class holds zone strings and searchable indice
1269: */
1270: private class ZoneItemInfo {
1271: String[][] tzStrings;
1272: HashMap tzidMap;
1273: TextTrieMap tzStringMap;
1274: }
1275:
1276: /**
1277: * A cache for ZoneItemInfo objects, shared by class instances.
1278: */
1279: private static SoftCache zoneItemInfoCache = new SoftCache();
1280:
1281: /**
1282: * A ZoneItemInfo instance which holds custom timezone strings
1283: */
1284: private transient ZoneItemInfo localZoneItemInfo;
1285:
1286: /**
1287: * Single entry cache for findZoneTypeValue()
1288: */
1289: private transient ZoneItem lastZoneItem;
1290:
1291: /**
1292: * Gets the ZoneItemInfo instance for the locale used by this object.
1293: * If it does not exist, create new one and register in the static cache.
1294: */
1295: private ZoneItemInfo getDefaultZoneItemInfo() {
1296: ZoneItemInfo zii = (ZoneItemInfo) zoneItemInfoCache
1297: .get(requestedLocale);
1298: if (zii != null) {
1299: return zii;
1300: }
1301: zii = getZoneItemInfo(getDefaultZoneStrings(requestedLocale));
1302: // Add fallback display names
1303: String[] zoneIDs = TimeZone.getAvailableIDs();
1304: for (int i = 0; i < zoneIDs.length; i++) {
1305: Object o = zii.tzidMap.get(zoneIDs[i]);
1306: if (o != null) {
1307: // Already has names
1308: continue;
1309: }
1310: String value = ZoneMeta.displayFallback(zoneIDs[i], null,
1311: requestedLocale);
1312: if (value != null) {
1313: String[] strings = new String[8];
1314: strings[5] = value;
1315: zii.tzidMap.put(zoneIDs[i], strings);
1316:
1317: ZoneItem item = new ZoneItem();
1318: item.zid = zoneIDs[i];
1319: item.value = value;
1320: item.type = TIMEZONE_EXEMPLAR_CITY;
1321: zii.tzStringMap.put(item.value, item);
1322: }
1323: }
1324: zoneItemInfoCache.put(requestedLocale, zii);
1325: return zii;
1326: }
1327:
1328: /**
1329: * Gets the array of zone strings for the specified locale.
1330: */
1331: private static String[][] getDefaultZoneStrings(ULocale locale) {
1332: ArrayList tmpList = new ArrayList();
1333: HashSet tmpSet = new HashSet();
1334: for (ULocale tempLocale = locale; tempLocale != null; tempLocale = tempLocale
1335: .getFallback()) {
1336: ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle
1337: .getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
1338: tempLocale);
1339: ICUResourceBundle zoneStringsBundle = bundle
1340: .getWithFallback("zoneStrings");
1341: for (int i = 0; i < zoneStringsBundle.getSize(); i++) {
1342: ICUResourceBundle zoneTable = zoneStringsBundle.get(i);
1343: String key = Utility.replaceAll(zoneTable.getKey(),
1344: ":", "/");
1345: // hack for the root zone strings
1346: if (key.length() == 0
1347: || zoneTable.getType() != ICUResourceBundle.TABLE) {
1348: continue;
1349: }
1350: if (tmpSet.contains(key)) {
1351: // only add if we don't have already
1352: continue;
1353: }
1354: String[] strings = new String[8];
1355: strings[0] = key;
1356: try {
1357: strings[1] = zoneTable
1358: .getStringWithFallback(LONG_STANDARD);
1359: } catch (MissingResourceException ex) {
1360: // throw away the exception
1361: }
1362: try {
1363: strings[2] = zoneTable
1364: .getStringWithFallback(SHORT_STANDARD);
1365: } catch (MissingResourceException ex) {
1366: // throw away the exception
1367: }
1368: try {
1369: strings[3] = zoneTable
1370: .getStringWithFallback(LONG_DAYLIGHT);
1371: } catch (MissingResourceException ex) {
1372: // throw away the exception
1373: }
1374: try {
1375: strings[4] = zoneTable
1376: .getStringWithFallback(SHORT_DAYLIGHT);
1377: } catch (MissingResourceException ex) {
1378: // throw away the exception
1379: }
1380: try {
1381: String city = zoneTable
1382: .getStringWithFallback(EXEMPLAR_CITY);
1383: strings[5] = ZoneMeta.displayFallback(key, city,
1384: tempLocale);
1385: } catch (MissingResourceException ex) {
1386: // throw away the exception
1387: }
1388: try {
1389: strings[6] = zoneTable
1390: .getStringWithFallback(LONG_GENERIC);
1391: } catch (MissingResourceException ex) {
1392: // throw away the exception
1393: }
1394: try {
1395: strings[7] = zoneTable
1396: .getStringWithFallback(SHORT_GENERIC);
1397: } catch (MissingResourceException ex) {
1398: // throw away the exception
1399: }
1400: tmpList.add(strings);
1401: }
1402: }
1403: String[][] array = new String[tmpList.size()][8];
1404: tmpList.toArray(array);
1405: return array;
1406: }
1407:
1408: /**
1409: * Gets the array of zone strings for the custom zone strings
1410: */
1411: private ZoneItemInfo getLocalZoneItemInfo() {
1412: if (localZoneItemInfo == null && zoneStrings != null) {
1413: localZoneItemInfo = getZoneItemInfo(zoneStrings);
1414: }
1415: return localZoneItemInfo;
1416: }
1417:
1418: /**
1419: * Creates a new ZoneItemInfo instance from the array of time zone
1420: * strings.
1421: */
1422: private ZoneItemInfo getZoneItemInfo(String[][] strings) {
1423: ZoneItemInfo zii = new ZoneItemInfo();
1424: zii.tzStrings = strings;
1425: zii.tzidMap = new HashMap();
1426: zii.tzStringMap = new TextTrieMap(true);
1427: for (int i = 0; i < strings.length; i++) {
1428: String zid = strings[i][0];
1429: if (zid != null && zid.length() > 0) {
1430: zii.tzidMap.put(zid, strings[i]);
1431: int nameCount = strings[i].length < 8 ? strings[i].length
1432: : 8;
1433: for (int j = 1; j < nameCount; j++) {
1434: if (strings[i][j] != null) {
1435: // map zoneStrings array index to timezone name type
1436: int type = -1;
1437: switch (j) {
1438: case 1:
1439: type = TIMEZONE_LONG_STANDARD;
1440: break;
1441: case 2:
1442: type = TIMEZONE_SHORT_STANDARD;
1443: break;
1444: case 3:
1445: type = TIMEZONE_LONG_DAYLIGHT;
1446: break;
1447: case 4:
1448: type = TIMEZONE_SHORT_DAYLIGHT;
1449: break;
1450: case 5:
1451: if (nameCount == 6 || nameCount == 8) {
1452: type = TIMEZONE_EXEMPLAR_CITY;
1453: } else {
1454: type = TIMEZONE_LONG_GENERIC;
1455: }
1456: break;
1457: case 6:
1458: if (nameCount == 8) {
1459: type = TIMEZONE_LONG_GENERIC;
1460: } else {
1461: type = TIMEZONE_SHORT_GENERIC;
1462: }
1463: break;
1464: case 7:
1465: type = TIMEZONE_SHORT_GENERIC;
1466: break;
1467: default:
1468: // never occur
1469: continue;
1470: }
1471: ZoneItem item = new ZoneItem();
1472: item.zid = zid;
1473: item.value = strings[i][j];
1474: item.type = type;
1475: zii.tzStringMap.put(strings[i][j], item);
1476: }
1477: }
1478: }
1479: }
1480: return zii;
1481: }
1482:
1483: /**
1484: * save the input locale
1485: */
1486: private ULocale requestedLocale;
1487:
1488: /**
1489: * The translation type of the translated zone strings
1490: * @internal ICU 3.6
1491: * @deprecated This API is ICU internal only.
1492: */
1493: private static final String SHORT_GENERIC = "sg",
1494: SHORT_STANDARD = "ss", SHORT_DAYLIGHT = "sd",
1495: LONG_GENERIC = "lg", LONG_STANDARD = "ls",
1496: LONG_DAYLIGHT = "ld", EXEMPLAR_CITY = "ec";
1497: /**
1498: * The translation type of the translated zone strings
1499: * @internal ICU 3.6
1500: * @deprecated This API is ICU internal only.
1501: */
1502: static final int TIMEZONE_SHORT_GENERIC = 0,
1503: TIMEZONE_SHORT_STANDARD = 1, TIMEZONE_SHORT_DAYLIGHT = 2,
1504: TIMEZONE_LONG_GENERIC = 3, TIMEZONE_LONG_STANDARD = 4,
1505: TIMEZONE_LONG_DAYLIGHT = 5, TIMEZONE_EXEMPLAR_CITY = 6,
1506: TIMEZONE_COUNT = 7;
1507:
1508: /**
1509: * Package private: used by SimpleDateFormat
1510: * Gets the index for the given time zone ID to obtain the timezone
1511: * strings for formatting. The time zone ID is just for programmatic
1512: * lookup. NOT LOCALIZED!!!
1513: * @param ID the given time zone ID.
1514: * @return the index of the given time zone ID. Returns -1 if
1515: * the given time zone ID can't be located in the DateFormatSymbols object.
1516: * @see com.ibm.icu.util.SimpleTimeZone
1517: */
1518: final int getZoneIndex(String ID) {
1519: int result = _getZoneIndex(ID);
1520: if (result >= 0) {
1521: return result;
1522: }
1523: // Do a search through the equivalency group for the given ID
1524: int n = ZoneMeta.countEquivalentIDs(ID);
1525: if (n > 1) {
1526: for (int i = 0; i < n; ++i) {
1527: String equivID = ZoneMeta.getEquivalentID(ID, i);
1528: if (!equivID.equals(ID)) {
1529: int equivResult = _getZoneIndex(equivID);
1530: if (equivResult >= 0) {
1531: return equivResult;
1532: }
1533: }
1534: }
1535: }
1536: return -1;
1537: }
1538:
1539: /**
1540: * Lookup the given ID. Do NOT do an equivalency search.
1541: */
1542: private int _getZoneIndex(String ID) {
1543: for (int index = 0; index < zoneStrings.length; index++) {
1544: if (ID.equalsIgnoreCase(zoneStrings[index][0]))
1545: return index;
1546: }
1547: return -1;
1548: }
1549:
1550: /**
1551: * Clones an array of Strings.
1552: * @param srcArray the source array to be cloned.
1553: * @param count the number of elements in the given source array.
1554: * @return a cloned array.
1555: */
1556: private final String[] duplicate(String[] srcArray) {
1557: return (String[]) srcArray.clone();
1558: }
1559:
1560: private final String[][] duplicate(String[][] srcArray) {
1561: String[][] aCopy = new String[srcArray.length][];
1562: for (int i = 0; i < srcArray.length; ++i)
1563: aCopy[i] = duplicate(srcArray[i]);
1564: return aCopy;
1565: }
1566:
1567: /**
1568: * Clones all the data members from the source DateFormatSymbols to
1569: * the target DateFormatSymbols. This is only for subclasses.
1570: * @param src the source DateFormatSymbols.
1571: * @param dst the target DateFormatSymbols.
1572: */
1573: private final void copyMembers(DateFormatSymbols src,
1574: DateFormatSymbols dst) {
1575: dst.eras = duplicate(src.eras);
1576: dst.eraNames = duplicate(src.eraNames);
1577: dst.months = duplicate(src.months);
1578: dst.shortMonths = duplicate(src.shortMonths);
1579: dst.narrowMonths = duplicate(src.narrowMonths);
1580: dst.standaloneMonths = duplicate(src.standaloneMonths);
1581: dst.standaloneShortMonths = duplicate(src.standaloneShortMonths);
1582: dst.standaloneNarrowMonths = duplicate(src.standaloneNarrowMonths);
1583: dst.weekdays = duplicate(src.weekdays);
1584: dst.shortWeekdays = duplicate(src.shortWeekdays);
1585: dst.narrowWeekdays = duplicate(src.narrowWeekdays);
1586: dst.standaloneWeekdays = duplicate(src.standaloneWeekdays);
1587: dst.standaloneShortWeekdays = duplicate(src.standaloneShortWeekdays);
1588: dst.standaloneNarrowWeekdays = duplicate(src.standaloneNarrowWeekdays);
1589: dst.ampms = duplicate(src.ampms);
1590: if (src.zoneStrings != null) {
1591: dst.zoneStrings = duplicate(src.zoneStrings);
1592: }
1593: dst.requestedLocale = new ULocale(src.requestedLocale
1594: .toString());
1595: dst.localPatternChars = new String(src.localPatternChars);
1596: }
1597:
1598: /**
1599: * Compares the equality of the two arrays of String.
1600: * @param current this String array.
1601: * @param other that String array.
1602: private final boolean equals(String[] current, String[] other)
1603: {
1604: int count = current.length;
1605:
1606: for (int i = 0; i < count; ++i)
1607: if (!current[i].equals(other[i]))
1608: return false;
1609: return true;
1610: }
1611: */
1612:
1613: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1614: /**
1615: * Get the {@link DateFormatSymbols} object that should be used to format a
1616: * calendar system's dates in the given locale.
1617: * <p>
1618: * <b>Subclassing:</b><br>
1619: * When creating a new Calendar subclass, you must create the
1620: * {@link ResourceBundle ResourceBundle}
1621: * containing its {@link DateFormatSymbols DateFormatSymbols} in a specific place.
1622: * The resource bundle name is based on the calendar's fully-specified
1623: * class name, with ".resources" inserted at the end of the package name
1624: * (just before the class name) and "Symbols" appended to the end.
1625: * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
1626: * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
1627: * <p>
1628: * Within the ResourceBundle, this method searches for five keys:
1629: * <ul>
1630: * <li><b>DayNames</b> -
1631: * An array of strings corresponding to each possible
1632: * value of the <code>DAY_OF_WEEK</code> field. Even though
1633: * <code>DAY_OF_WEEK</code> starts with <code>SUNDAY</code> = 1,
1634: * This array is 0-based; the name for Sunday goes in the
1635: * first position, at index 0. If this key is not found
1636: * in the bundle, the day names are inherited from the
1637: * default <code>DateFormatSymbols</code> for the requested locale.
1638: *
1639: * <li><b>DayAbbreviations</b> -
1640: * An array of abbreviated day names corresponding
1641: * to the values in the "DayNames" array. If this key
1642: * is not found in the resource bundle, the "DayNames"
1643: * values are used instead. If neither key is found,
1644: * the day abbreviations are inherited from the default
1645: * <code>DateFormatSymbols</code> for the locale.
1646: *
1647: * <li><b>MonthNames</b> -
1648: * An array of strings corresponding to each possible
1649: * value of the <code>MONTH</code> field. If this key is not found
1650: * in the bundle, the month names are inherited from the
1651: * default <code>DateFormatSymbols</code> for the requested locale.
1652: *
1653: * <li><b>MonthAbbreviations</b> -
1654: * An array of abbreviated day names corresponding
1655: * to the values in the "MonthNames" array. If this key
1656: * is not found in the resource bundle, the "MonthNames"
1657: * values are used instead. If neither key is found,
1658: * the day abbreviations are inherited from the default
1659: * <code>DateFormatSymbols</code> for the locale.
1660: *
1661: * <li><b>Eras</b> -
1662: * An array of strings corresponding to each possible
1663: * value of the <code>ERA</code> field. If this key is not found
1664: * in the bundle, the era names are inherited from the
1665: * default <code>DateFormatSymbols</code> for the requested locale.
1666: * </ul>
1667: * <p>
1668: * @param cal The calendar system whose date format symbols are desired.
1669: * @param locale The locale whose symbols are desired.
1670: *
1671: * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
1672: * @stable ICU 2.0
1673: */
1674: public DateFormatSymbols(Calendar cal, Locale locale) {
1675: initializeData(ULocale.forLocale(locale), cal.getType());
1676: }
1677:
1678: /**
1679: * Get the {@link DateFormatSymbols} object that should be used to format a
1680: * calendar system's dates in the given locale.
1681: * <p>
1682: * <b>Subclassing:</b><br>
1683: * When creating a new Calendar subclass, you must create the
1684: * {@link ResourceBundle ResourceBundle}
1685: * containing its {@link DateFormatSymbols DateFormatSymbols} in a specific place.
1686: * The resource bundle name is based on the calendar's fully-specified
1687: * class name, with ".resources" inserted at the end of the package name
1688: * (just before the class name) and "Symbols" appended to the end.
1689: * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
1690: * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
1691: * <p>
1692: * Within the ResourceBundle, this method searches for five keys:
1693: * <ul>
1694: * <li><b>DayNames</b> -
1695: * An array of strings corresponding to each possible
1696: * value of the <code>DAY_OF_WEEK</code> field. Even though
1697: * <code>DAY_OF_WEEK</code> starts with <code>SUNDAY</code> = 1,
1698: * This array is 0-based; the name for Sunday goes in the
1699: * first position, at index 0. If this key is not found
1700: * in the bundle, the day names are inherited from the
1701: * default <code>DateFormatSymbols</code> for the requested locale.
1702: *
1703: * <li><b>DayAbbreviations</b> -
1704: * An array of abbreviated day names corresponding
1705: * to the values in the "DayNames" array. If this key
1706: * is not found in the resource bundle, the "DayNames"
1707: * values are used instead. If neither key is found,
1708: * the day abbreviations are inherited from the default
1709: * <code>DateFormatSymbols</code> for the locale.
1710: *
1711: * <li><b>MonthNames</b> -
1712: * An array of strings corresponding to each possible
1713: * value of the <code>MONTH</code> field. If this key is not found
1714: * in the bundle, the month names are inherited from the
1715: * default <code>DateFormatSymbols</code> for the requested locale.
1716: *
1717: * <li><b>MonthAbbreviations</b> -
1718: * An array of abbreviated day names corresponding
1719: * to the values in the "MonthNames" array. If this key
1720: * is not found in the resource bundle, the "MonthNames"
1721: * values are used instead. If neither key is found,
1722: * the day abbreviations are inherited from the default
1723: * <code>DateFormatSymbols</code> for the locale.
1724: *
1725: * <li><b>Eras</b> -
1726: * An array of strings corresponding to each possible
1727: * value of the <code>ERA</code> field. If this key is not found
1728: * in the bundle, the era names are inherited from the
1729: * default <code>DateFormatSymbols</code> for the requested locale.
1730: * </ul>
1731: * <p>
1732: * @param cal The calendar system whose date format symbols are desired.
1733: * @param locale The ulocale whose symbols are desired.
1734: *
1735: * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
1736: * @draft ICU 3.2
1737: * @provisional This API might change or be removed in a future release.
1738: */
1739: public DateFormatSymbols(Calendar cal, ULocale locale) {
1740: initializeData(locale, cal.getType());
1741: }
1742:
1743: /**
1744: * Variant of DateFormatSymbols(Calendar, Locale) that takes the Calendar class
1745: * instead of a Calandar instance.
1746: * @see #DateFormatSymbols(Calendar, Locale)
1747: * @stable ICU 2.2
1748: */
1749: public DateFormatSymbols(Class calendarClass, Locale locale) {
1750: this (calendarClass, ULocale.forLocale(locale));
1751: }
1752:
1753: /**
1754: * Variant of DateFormatSymbols(Calendar, ULocale) that takes the Calendar class
1755: * instead of a Calandar instance.
1756: * @see #DateFormatSymbols(Calendar, Locale)
1757: * @draft ICU 3.2
1758: * @provisional This API might change or be removed in a future release.
1759: */
1760: public DateFormatSymbols(Class calendarClass, ULocale locale) {
1761: String fullName = calendarClass.getName();
1762: int lastDot = fullName.lastIndexOf('.');
1763: String className = fullName.substring(lastDot + 1);
1764: String calType = Utility.replaceAll(className, "Calendar", "")
1765: .toLowerCase();
1766:
1767: initializeData(locale, calType);
1768: }
1769:
1770: /**
1771: * Fetch a custom calendar's DateFormatSymbols out of the given resource
1772: * bundle. Symbols that are not overridden are inherited from the
1773: * default DateFormatSymbols for the locale.
1774: * @see DateFormatSymbols#DateFormatSymbols
1775: * @stable ICU 2.0
1776: */
1777: public DateFormatSymbols(ResourceBundle bundle, Locale locale) {
1778: this (bundle, ULocale.forLocale(locale));
1779: }
1780:
1781: /**
1782: * Fetch a custom calendar's DateFormatSymbols out of the given resource
1783: * bundle. Symbols that are not overridden are inherited from the
1784: * default DateFormatSymbols for the locale.
1785: * @see DateFormatSymbols#DateFormatSymbols
1786: * @draft ICU 3.2
1787: * @provisional This API might change or be removed in a future release.
1788: */
1789: public DateFormatSymbols(ResourceBundle bundle, ULocale locale) {
1790: initializeData(locale, new CalendarData(
1791: (ICUResourceBundle) bundle, null));
1792: }
1793:
1794: /**
1795: * Find the ResourceBundle containing the date format information for
1796: * a specified calendar subclass in a given locale.
1797: * <p>
1798: * The resource bundle name is based on the calendar's fully-specified
1799: * class name, with ".resources" inserted at the end of the package name
1800: * (just before the class name) and "Symbols" appended to the end.
1801: * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
1802: * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
1803: * @stable ICU 2.0
1804: */
1805: static public ResourceBundle getDateFormatBundle(
1806: Class calendarClass, Locale locale)
1807: throws MissingResourceException {
1808: return getDateFormatBundle(calendarClass, ULocale
1809: .forLocale(locale));
1810: }
1811:
1812: /**
1813: * Find the ResourceBundle containing the date format information for
1814: * a specified calendar subclass in a given locale.
1815: * <p>
1816: * The resource bundle name is based on the calendar's fully-specified
1817: * class name, with ".resources" inserted at the end of the package name
1818: * (just before the class name) and "Symbols" appended to the end.
1819: * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
1820: * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
1821: * @draft ICU 3.2
1822: * @provisional This API might change or be removed in a future release.
1823: */
1824: static public ResourceBundle getDateFormatBundle(
1825: Class calendarClass, ULocale locale)
1826: throws MissingResourceException {
1827:
1828: // Find the calendar's class name, which we're going to use to construct the
1829: // resource bundle name.
1830: String fullName = calendarClass.getName();
1831: int lastDot = fullName.lastIndexOf('.');
1832: String className = fullName.substring(lastDot + 1);
1833:
1834: String bundleName = className + "Symbols";
1835:
1836: UResourceBundle result = null;
1837: try {
1838: result = UResourceBundle.getBundleInstance(bundleName,
1839: locale);
1840: } catch (MissingResourceException e) {
1841: ///CLOVER:OFF
1842: // coverage requires test without data, so skip
1843: //if (!(cal instanceof GregorianCalendar)) {
1844: if (!(GregorianCalendar.class
1845: .isAssignableFrom(calendarClass))) {
1846: // Ok for symbols to be missing for a Gregorian calendar, but
1847: // not for any other type.
1848: throw e;
1849: }
1850: ///CLOVER:ON
1851: }
1852: return result;
1853: }
1854:
1855: /**
1856: * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes
1857: * a Calendar instance instead of a Calendar class.
1858: * @see #getDateFormatBundle(java.lang.Class, java.util.Locale)
1859: * @stable ICU 2.2
1860: */
1861: public static ResourceBundle getDateFormatBundle(Calendar cal,
1862: Locale locale) throws MissingResourceException {
1863: return getDateFormatBundle(cal.getClass(), ULocale
1864: .forLocale(locale));
1865: }
1866:
1867: /**
1868: * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes
1869: * a Calendar instance instead of a Calendar class.
1870: * @see #getDateFormatBundle(java.lang.Class, java.util.Locale)
1871: * @draft ICU 3.2
1872: * @provisional This API might change or be removed in a future release.
1873: */
1874: public static ResourceBundle getDateFormatBundle(Calendar cal,
1875: ULocale locale) throws MissingResourceException {
1876: return getDateFormatBundle(cal.getClass(), locale);
1877: }
1878:
1879: // -------- BEGIN ULocale boilerplate --------
1880:
1881: /**
1882: * Return the locale that was used to create this object, or null.
1883: * This may may differ from the locale requested at the time of
1884: * this object's creation. For example, if an object is created
1885: * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be
1886: * drawn from <tt>en</tt> (the <i>actual</i> locale), and
1887: * <tt>en_US</tt> may be the most specific locale that exists (the
1888: * <i>valid</i> locale).
1889: *
1890: * <p>Note: This method will be implemented in ICU 3.0; ICU 2.8
1891: * contains a partial preview implementation. The * <i>actual</i>
1892: * locale is returned correctly, but the <i>valid</i> locale is
1893: * not, in most cases.
1894: * @param type type of information requested, either {@link
1895: * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link
1896: * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}.
1897: * @return the information specified by <i>type</i>, or null if
1898: * this object was not constructed from locale data.
1899: * @see com.ibm.icu.util.ULocale
1900: * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1901: * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1902: * @draft ICU 2.8 (retain)
1903: * @provisional This API might change or be removed in a future release.
1904: */
1905: public final ULocale getLocale(ULocale.Type type) {
1906: return type == ULocale.ACTUAL_LOCALE ? this .actualLocale
1907: : this .validLocale;
1908: }
1909:
1910: /**
1911: * Set information about the locales that were used to create this
1912: * object. If the object was not constructed from locale data,
1913: * both arguments should be set to null. Otherwise, neither
1914: * should be null. The actual locale must be at the same level or
1915: * less specific than the valid locale. This method is intended
1916: * for use by factories or other entities that create objects of
1917: * this class.
1918: * @param valid the most specific locale containing any resource
1919: * data, or null
1920: * @param actual the locale containing data used to construct this
1921: * object, or null
1922: * @see com.ibm.icu.util.ULocale
1923: * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1924: * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1925: * @internal
1926: * @deprecated This API is ICU internal only.
1927: */
1928: final void setLocale(ULocale valid, ULocale actual) {
1929: // Change the following to an assertion later
1930: if ((valid == null) != (actual == null)) {
1931: ///CLOVER:OFF
1932: throw new IllegalArgumentException();
1933: ///CLOVER:ON
1934: }
1935: // Another check we could do is that the actual locale is at
1936: // the same level or less specific than the valid locale.
1937: this .validLocale = valid;
1938: this .actualLocale = actual;
1939: }
1940:
1941: /**
1942: * The most specific locale containing any resource data, or null.
1943: * @see com.ibm.icu.util.ULocale
1944: * @internal
1945: * @deprecated This API is ICU internal only.
1946: */
1947: private ULocale validLocale;
1948:
1949: /**
1950: * The locale containing data used to construct this object, or
1951: * null.
1952: * @see com.ibm.icu.util.ULocale
1953: * @internal
1954: * @deprecated This API is ICU internal only.
1955: */
1956: private ULocale actualLocale;
1957:
1958: // -------- END ULocale boilerplate --------
1959: }
|