1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3/* 4 ******************************************************************************* 5 * Copyright (C) 1996-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 10package com.ibm.icu.text; 11 12import java.io.IOException; 13import java.io.ObjectInputStream; 14import java.io.Serializable; 15import java.util.ArrayList; 16import java.util.HashMap; 17import java.util.HashSet; 18import java.util.List; 19import java.util.Locale; 20import java.util.Map; 21import java.util.MissingResourceException; 22import java.util.ResourceBundle; 23import java.util.Set; 24import java.util.TreeMap; 25 26import com.ibm.icu.impl.CacheBase; 27import com.ibm.icu.impl.CalendarUtil; 28import com.ibm.icu.impl.ICUData; 29import com.ibm.icu.impl.ICUResourceBundle; 30import com.ibm.icu.impl.SoftCache; 31import com.ibm.icu.impl.UResource; 32import com.ibm.icu.impl.Utility; 33import com.ibm.icu.text.TimeZoneNames.NameType; 34import com.ibm.icu.util.Calendar; 35import com.ibm.icu.util.ICUCloneNotSupportedException; 36import com.ibm.icu.util.ICUException; 37import com.ibm.icu.util.TimeZone; 38import com.ibm.icu.util.ULocale; 39import com.ibm.icu.util.ULocale.Category; 40import com.ibm.icu.util.UResourceBundle; 41import com.ibm.icu.util.UResourceBundleIterator; 42 43/** 44 * {@icuenhanced java.text.DateFormatSymbols}.{@icu _usage_} 45 * 46 * <p><code>DateFormatSymbols</code> is a public class for encapsulating 47 * localizable date-time formatting data, such as the names of the 48 * months, the names of the days of the week, and the time zone data. 49 * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use 50 * <code>DateFormatSymbols</code> to encapsulate this information. 51 * 52 * <p>Typically you shouldn't use <code>DateFormatSymbols</code> directly. 53 * Rather, you are encouraged to create a date-time formatter with the 54 * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>, 55 * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>. 56 * These methods automatically create a <code>DateFormatSymbols</code> for 57 * the formatter so that you don't have to. After the 58 * formatter is created, you may modify its format pattern using the 59 * <code>setPattern</code> method. For more information about 60 * creating formatters using <code>DateFormat</code>'s factory methods, 61 * see {@link DateFormat}. 62 * 63 * <p>If you decide to create a date-time formatter with a specific 64 * format pattern for a specific locale, you can do so with: 65 * <blockquote> 66 * <pre> 67 * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)). 68 * </pre> 69 * </blockquote> 70 * 71 * <p><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>New <code>DateFormatSymbols</code> subclasses may be added to support 79 * <code>SimpleDateFormat</code> for date-time formatting for additional locales. 80 * 81 * @see DateFormat 82 * @see SimpleDateFormat 83 * @see com.ibm.icu.util.SimpleTimeZone 84 * @author Chen-Lieh Huang 85 * @stable ICU 2.0 86 */ 87public class DateFormatSymbols implements Serializable, Cloneable { 88 89 // TODO make sure local pattern char string is 18 characters long, 90 // that is, that it encompasses the new 'u' char for 91 // EXTENDED_YEAR. Two options: 1. Make sure resource data is 92 // correct; 2. Make code add in 'u' at end if len == 17. 93 94 // Constants for context 95 /** 96 * {@icu} Constant for context. 97 * @stable ICU 3.6 98 */ 99 public static final int FORMAT = 0; 100 101 /** 102 * {@icu} Constant for context. 103 * @stable ICU 3.6 104 */ 105 public static final int STANDALONE = 1; 106 107 /** 108 * {@icu} Constant for context. NUMERIC context 109 * is only supported for leapMonthPatterns. 110 * @internal 111 * @deprecated This API is ICU internal only. 112 */ 113 @Deprecated 114 public static final int NUMERIC = 2; 115 116 /** 117 * {@icu} Constant for context. 118 * @internal 119 * @deprecated This API is ICU internal only. 120 */ 121 @Deprecated 122 public static final int DT_CONTEXT_COUNT = 3; 123 124 // Constants for width 125 126 /** 127 * {@icu} Constant for width. 128 * @stable ICU 3.6 129 */ 130 public static final int ABBREVIATED = 0; 131 132 /** 133 * {@icu} Constant for width. 134 * @stable ICU 3.6 135 */ 136 public static final int WIDE = 1; 137 138 /** 139 * {@icu} Constant for width. 140 * @stable ICU 3.6 141 */ 142 public static final int NARROW = 2; 143 144 /** 145 * {@icu} Constant for width; only supported for weekdays. 146 * @stable ICU 51 147 */ 148 public static final int SHORT = 3; 149 150 /** 151 * {@icu} Constant for width. 152 * @internal 153 * @deprecated This API is ICU internal only. 154 */ 155 @Deprecated 156 public static final int DT_WIDTH_COUNT = 4; 157 158 /** 159 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 160 * @internal 161 */ 162 static final int DT_LEAP_MONTH_PATTERN_FORMAT_WIDE = 0; 163 164 /** 165 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 166 * @internal 167 */ 168 static final int DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV = 1; 169 170 /** 171 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 172 * @internal 173 */ 174 static final int DT_LEAP_MONTH_PATTERN_FORMAT_NARROW = 2; 175 176 /** 177 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 178 * @internal 179 */ 180 static final int DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE = 3; 181 182 /** 183 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 184 * @internal 185 */ 186 static final int DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV = 4; 187 188 /** 189 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 190 * @internal 191 */ 192 static final int DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW = 5; 193 194 /** 195 * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar. 196 * @internal 197 */ 198 static final int DT_LEAP_MONTH_PATTERN_NUMERIC = 6; 199 200 /** 201 * {@icu} Somewhat temporary constant for month pattern count, adequate for Chinese calendar. 202 * @internal 203 */ 204 static final int DT_MONTH_PATTERN_COUNT = 7; 205 206 /** 207 * {@icu} This default time separator is used for formatting when the locale 208 * doesn't specify any time separator, and always recognized when parsing. 209 * @internal 210 */ 211 static final String DEFAULT_TIME_SEPARATOR = ":"; 212 213 /** 214 * {@icu} This alternate time separator is always recognized when parsing. 215 * @internal 216 */ 217 static final String ALTERNATE_TIME_SEPARATOR = "."; 218 219 /** 220 * Constructs a DateFormatSymbols object by loading format data from 221 * resources for the default <code>FORMAT</code> locale. 222 * 223 * @throws java.util.MissingResourceException if the resources for the default locale 224 * cannot be found or cannot be loaded. 225 * @see Category#FORMAT 226 * @stable ICU 2.0 227 */ 228 public DateFormatSymbols() 229 { 230 this(ULocale.getDefault(Category.FORMAT)); 231 } 232 233 /** 234 * Constructs a DateFormatSymbols object by loading format data from 235 * resources for the given locale. 236 * 237 * @throws java.util.MissingResourceException if the resources for the specified 238 * locale cannot be found or cannot be loaded. 239 * @stable ICU 2.0 240 */ 241 public DateFormatSymbols(Locale locale) 242 { 243 this(ULocale.forLocale(locale)); 244 } 245 246 /** 247 * {@icu} Constructs a DateFormatSymbols object by loading format data from 248 * resources for the given ulocale. 249 * 250 * @throws java.util.MissingResourceException if the resources for the specified 251 * locale cannot be found or cannot be loaded. 252 * @stable ICU 3.2 253 */ 254 public DateFormatSymbols(ULocale locale) 255 { 256 initializeData(locale, CalendarUtil.getCalendarType(locale)); 257 } 258 259 /** 260 * Returns a DateFormatSymbols instance for the default locale. 261 * 262 * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>, 263 * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols()</code>. 264 * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6 265 * or its equivalent implementation for now. 266 * 267 * @return A DateFormatSymbols instance. 268 * @stable ICU 3.8 269 */ 270 public static DateFormatSymbols getInstance() { 271 return new DateFormatSymbols(); 272 } 273 274 /** 275 * Returns a DateFormatSymbols instance for the given locale. 276 * 277 * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>, 278 * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols(locale)</code>. 279 * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6 280 * or its equivalent implementation for now. 281 * 282 * @param locale the locale. 283 * @return A DateFormatSymbols instance. 284 * @stable ICU 3.8 285 */ 286 public static DateFormatSymbols getInstance(Locale locale) { 287 return new DateFormatSymbols(locale); 288 } 289 290 /** 291 * {@icu} Returns a DateFormatSymbols instance for the given locale. 292 * 293 * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>, 294 * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols(locale)</code>. 295 * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6 296 * or its equivalent implementation for now. 297 * 298 * @param locale the locale. 299 * @return A DateFormatSymbols instance. 300 * @stable ICU 3.8 301 */ 302 public static DateFormatSymbols getInstance(ULocale locale) { 303 return new DateFormatSymbols(locale); 304 } 305 306 /** 307 * Returns an array of all locales for which the <code>getInstance</code> methods of 308 * this class can return localized instances. 309 * 310 * {@icunote} Unlike <code>java.text.DateFormatSymbols#getAvailableLocales</code>, 311 * this method simply returns the array of <code>Locale</code>s available in this 312 * class. ICU does not support <code>DateFormatSymbolsProvider</code> introduced in 313 * Java 6 or its equivalent implementation for now. 314 * 315 * @return An array of <code>Locale</code>s for which localized 316 * <code>DateFormatSymbols</code> instances are available. 317 * @stable ICU 3.8 318 */ 319 public static Locale[] getAvailableLocales() { 320 return ICUResourceBundle.getAvailableLocales(); 321 } 322 323 /** 324 * {@icu} Returns an array of all locales for which the <code>getInstance</code> 325 * methods of this class can return localized instances. 326 * 327 * {@icunote} Unlike <code>java.text.DateFormatSymbols#getAvailableLocales</code>, 328 * this method simply returns the array of <code>ULocale</code>s available in this 329 * class. ICU does not support <code>DateFormatSymbolsProvider</code> introduced in 330 * Java 6 or its equivalent implementation for now. 331 * 332 * @return An array of <code>ULocale</code>s for which localized 333 * <code>DateFormatSymbols</code> instances are available. 334 * @draft ICU 3.8 (retain) 335 * @provisional This API might change or be removed in a future release. 336 */ 337 public static ULocale[] getAvailableULocales() { 338 return ICUResourceBundle.getAvailableULocales(); 339 } 340 341 /** 342 * Era strings. For example: "AD" and "BC". An array of 2 strings, 343 * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 344 * @serial 345 */ 346 String eras[] = null; 347 348 /** 349 * Era name strings. For example: "Anno Domini" and "Before Christ". An array of 2 strings, 350 * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 351 * @serial 352 */ 353 String eraNames[] = null; 354 355 /** 356 * Narrow era names. For example: "A" and "B". An array of 2 strings, 357 * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 358 * @serial 359 */ 360 String narrowEras[] = null; 361 362 /** 363 * Month strings. For example: "January", "February", etc. An array 364 * of 13 strings (some calendars have 13 months), indexed by 365 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 366 * @serial 367 */ 368 String months[] = null; 369 370 /** 371 * Short month strings. For example: "Jan", "Feb", etc. An array of 372 * 13 strings (some calendars have 13 months), indexed by 373 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 374 375 * @serial 376 */ 377 String shortMonths[] = null; 378 379 /** 380 * Narrow month strings. For example: "J", "F", etc. An array of 381 * 13 strings (some calendars have 13 months), indexed by 382 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 383 384 * @serial 385 */ 386 String narrowMonths[] = null; 387 388 /** 389 * Standalone month strings. For example: "January", "February", etc. An array 390 * of 13 strings (some calendars have 13 months), indexed by 391 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 392 * @serial 393 */ 394 String standaloneMonths[] = null; 395 396 /** 397 * Standalone short month strings. For example: "Jan", "Feb", etc. An array of 398 * 13 strings (some calendars have 13 months), indexed by 399 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 400 401 * @serial 402 */ 403 String standaloneShortMonths[] = null; 404 405 /** 406 * Standalone narrow month strings. For example: "J", "F", etc. An array of 407 * 13 strings (some calendars have 13 months), indexed by 408 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 409 410 * @serial 411 */ 412 String standaloneNarrowMonths[] = null; 413 414 /** 415 * Format wide weekday strings, for example: "Sunday", "Monday", etc. 416 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 417 * <code>Calendar.MONDAY</code>, etc. 418 * The element <code>weekdays[0]</code> is ignored. 419 * @serial 420 */ 421 String weekdays[] = null; 422 423 /** 424 * CLDR-style format abbreviated (not short) weekday strings, 425 * for example: "Sun", "Mon", etc. 426 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 427 * <code>Calendar.MONDAY</code>, etc. 428 * The element <code>shortWeekdays[0]</code> is ignored. 429 * @serial 430 */ 431 String shortWeekdays[] = null; 432 433 /** 434 * CLDR-style format short weekday strings, for example: "Su", "Mo", etc. 435 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 436 * <code>Calendar.MONDAY</code>, etc. 437 * The element <code>shorterWeekdays[0]</code> is ignored. 438 * @serial 439 */ 440 // Note, serialization restore from pre-ICU-51 will leave this null. 441 String shorterWeekdays[] = null; 442 443 /** 444 * CLDR-style format narrow weekday strings, for example: "S", "M", etc. 445 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 446 * <code>Calendar.MONDAY</code>, etc. 447 * The element <code>narrowWeekdays[0]</code> is ignored. 448 * @serial 449 */ 450 String narrowWeekdays[] = null; 451 452 /** 453 * Standalone wide weekday strings. For example: "Sunday", "Monday", etc. 454 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 455 * <code>Calendar.MONDAY</code>, etc. 456 * The element <code>standaloneWeekdays[0]</code> is ignored. 457 * @serial 458 */ 459 String standaloneWeekdays[] = null; 460 461 /** 462 * CLDR-style standalone abbreviated (not short) weekday strings, 463 * for example: "Sun", "Mon", etc. 464 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 465 * <code>Calendar.MONDAY</code>, etc. 466 * The element <code>standaloneShortWeekdays[0]</code> is ignored. 467 * @serial 468 */ 469 String standaloneShortWeekdays[] = null; 470 471 /** 472 * CLDR-style standalone short weekday strings, for example: "Sun", "Mon", etc. 473 * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 474 * <code>Calendar.MONDAY</code>, etc. 475 * The element <code>standaloneShorterWeekdays[0]</code> is ignored. 476 * @serial 477 */ 478 // Note, serialization restore from pre-ICU-51 will leave this null. 479 String standaloneShorterWeekdays[] = null; 480 481 /** 482 * Standalone narrow weekday strings. For example: "S", "M", etc. An array 483 * of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 484 * <code>Calendar.MONDAY</code>, etc. 485 * The element <code>standaloneNarrowWeekdays[0]</code> is ignored. 486 * @serial 487 */ 488 String standaloneNarrowWeekdays[] = null; 489 490 /** 491 * AM and PM strings. For example: "AM" and "PM". An array of 492 * 2 strings, indexed by <code>Calendar.AM</code> and 493 * <code>Calendar.PM</code>. 494 * @serial 495 */ 496 String ampms[] = null; 497 498 /** 499 * narrow AM and PM strings. For example: "a" and "p". An array of 500 * 2 strings, indexed by <code>Calendar.AM</code> and 501 * <code>Calendar.PM</code>. 502 * @serial 503 */ 504 String ampmsNarrow[] = null; 505 506 /** 507 * Time separator string. For example: ":". 508 * @serial 509 */ 510 private String timeSeparator = null; 511 512 /** 513 * Abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array 514 * of 4 strings indexed by the month divided by 3. 515 * @serial 516 */ 517 String shortQuarters[] = null; 518 519 /** 520 * Full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter", 521 * "4th Quarter". An array of 4 strings, indexed by the month divided by 3. 522 * @serial 523 */ 524 String quarters[] = null; 525 526 /** 527 * Standalone abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array 528 * of 4 strings indexed by the month divided by 3. 529 * @serial 530 */ 531 String standaloneShortQuarters[] = null; 532 533 /** 534 * Standalone full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter", 535 * "4th Quarter". An array of 4 strings, indexed by the month divided by 3. 536 * @serial 537 */ 538 String standaloneQuarters[] = null; 539 540 /** 541 * All leap month patterns, for example "{0}bis". 542 * An array of DT_MONTH_PATTERN_COUNT strings, indexed by the DT_LEAP_MONTH_PATTERN_XXX value. 543 * @serial 544 */ 545 String leapMonthPatterns[] = null; 546 547 /** 548 * Cyclic year names, for example: "jia-zi", "yi-chou", ... "gui-hai". 549 * An array of (normally) 60 strings, corresponding to cyclic years 1-60 (in Calendar YEAR field). 550 * Currently we only have data for format/abbreviated. 551 * For the others, just get from format/abbreviated, ignore set. 552 * @serial 553 */ 554 String shortYearNames[] = null; 555 556 /** 557 * Cyclic zodiac names, for example: "Rat", "Ox", "Tiger", etc. 558 * An array of (normally) 12 strings. 559 * Currently we only have data for format/abbreviated. 560 * For the others, just get from format/abbreviated, ignore set. 561 * @serial 562 */ 563 String shortZodiacNames[] = null; 564 565 /** 566 * Localized names of time zones in this locale. This is a 567 * two-dimensional array of strings of size <em>n</em> by <em>m</em>, 568 * where <em>m</em> is at least 5 and up to 7. Each of the <em>n</em> rows is an 569 * entry containing the localized names for a single <code>TimeZone</code>. 570 * Each such row contains (with <code>i</code> ranging from 571 * 0..<em>n</em>-1): 572 * <ul> 573 * <li><code>zoneStrings[i][0]</code> - time zone ID</li> 574 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard 575 * time</li> 576 * <li><code>zoneStrings[i][2]</code> - short name of zone in 577 * standard time</li> 578 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight 579 * savings time</li> 580 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight 581 * savings time</li> 582 * <li><code>zoneStrings[i][5]</code> - location name of zone</li> 583 * <li><code>zoneStrings[i][6]</code> - long generic name of zone</li> 584 * <li><code>zoneStrings[i][7]</code> - short generic of zone</li> 585 * </ul> 586 * The zone ID is <em>not</em> localized; it corresponds to the ID 587 * value associated with a system time zone object. All other entries 588 * are localized names. If a zone does not implement daylight savings 589 * time, the daylight savings time names are ignored. 590 * <em>Note:</em>CLDR 1.5 introduced metazone and its historical mappings. 591 * This simple two-dimensional array is no longer sufficient to represent 592 * localized names and its historic changes. Since ICU 3.8.1, localized 593 * zone names extracted from ICU locale data is stored in a ZoneStringFormat 594 * instance. But we still need to support the old way of customizing 595 * localized zone names, so we keep this field for the purpose. 596 * @see com.ibm.icu.util.TimeZone 597 * @serial 598 */ 599 private String zoneStrings[][] = null; 600 601 /** 602 * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. 603 * All locales use the same unlocalized pattern characters. 604 */ 605 static final String patternChars = "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB"; 606 607 /** 608 * Localized date-time pattern characters. For example, a locale may 609 * wish to use 'u' rather than 'y' to represent years in its date format 610 * pattern strings. 611 * This string must be exactly 18 characters long, with the index of 612 * the characters described by <code>DateFormat.ERA_FIELD</code>, 613 * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were 614 * "Xz...", then localized patterns would use 'X' for era and 'z' for year. 615 * @serial 616 */ 617 String localPatternChars = null; 618 619 /** 620 * Localized names for abbreviated (== short) day periods. 621 * An array of strings, in the order of DayPeriod constants. 622 */ 623 String abbreviatedDayPeriods[] = null; 624 625 /** 626 * Localized names for wide day periods. 627 * An array of strings, in the order of DayPeriod constants. 628 */ 629 String wideDayPeriods[] = null; 630 631 /** 632 * Localized names for narrow day periods. 633 * An array of strings, in the order of DayPeriod constants. 634 */ 635 String narrowDayPeriods[] = null; 636 637 /** 638 * Localized names for standalone abbreviated (== short) day periods. 639 * An array of strings, in the order of DayPeriod constants. 640 */ 641 String standaloneAbbreviatedDayPeriods[] = null; 642 643 /** 644 * Localized names for standalone wide day periods. 645 * An array of strings, in the order of DayPeriod constants. 646 */ 647 String standaloneWideDayPeriods[] = null; 648 649 /** 650 * Localized names for standalone narrow day periods. 651 * An array of strings, in the order of DayPeriod constants. 652 */ 653 String standaloneNarrowDayPeriods[] = null; 654 655 /* use serialVersionUID from JDK 1.1.4 for interoperability */ 656 private static final long serialVersionUID = -5987973545549424702L; 657 658 private static final String[][] CALENDAR_CLASSES = { 659 {"GregorianCalendar", "gregorian"}, 660 {"JapaneseCalendar", "japanese"}, 661 {"BuddhistCalendar", "buddhist"}, 662 {"TaiwanCalendar", "roc"}, 663 {"PersianCalendar", "persian"}, 664 {"IslamicCalendar", "islamic"}, 665 {"HebrewCalendar", "hebrew"}, 666 {"ChineseCalendar", "chinese"}, 667 {"IndianCalendar", "indian"}, 668 {"CopticCalendar", "coptic"}, 669 {"EthiopicCalendar", "ethiopic"}, 670 }; 671 672 /** 673 * {@icu} Constants for capitalization context usage types 674 * related to date formatting. 675 * @internal 676 */ 677 enum CapitalizationContextUsage { 678 OTHER, 679 MONTH_FORMAT, /* except narrow */ 680 MONTH_STANDALONE, /* except narrow */ 681 MONTH_NARROW, 682 DAY_FORMAT, /* except narrow */ 683 DAY_STANDALONE, /* except narrow */ 684 DAY_NARROW, 685 ERA_WIDE, 686 ERA_ABBREV, 687 ERA_NARROW, 688 ZONE_LONG, 689 ZONE_SHORT, 690 METAZONE_LONG, 691 METAZONE_SHORT 692 } 693 694 /** Map from resource key to CapitalizationContextUsage value 695 */ 696 private static final Map<String, CapitalizationContextUsage> contextUsageTypeMap; 697 static { 698 contextUsageTypeMap=new HashMap<String, CapitalizationContextUsage>(); 699 contextUsageTypeMap.put("month-format-except-narrow", CapitalizationContextUsage.MONTH_FORMAT); 700 contextUsageTypeMap.put("month-standalone-except-narrow", CapitalizationContextUsage.MONTH_STANDALONE); 701 contextUsageTypeMap.put("month-narrow", CapitalizationContextUsage.MONTH_NARROW); 702 contextUsageTypeMap.put("day-format-except-narrow", CapitalizationContextUsage.DAY_FORMAT); 703 contextUsageTypeMap.put("day-standalone-except-narrow", CapitalizationContextUsage.DAY_STANDALONE); 704 contextUsageTypeMap.put("day-narrow", CapitalizationContextUsage.DAY_NARROW); 705 contextUsageTypeMap.put("era-name", CapitalizationContextUsage.ERA_WIDE); 706 contextUsageTypeMap.put("era-abbr", CapitalizationContextUsage.ERA_ABBREV); 707 contextUsageTypeMap.put("era-narrow", CapitalizationContextUsage.ERA_NARROW); 708 contextUsageTypeMap.put("zone-long", CapitalizationContextUsage.ZONE_LONG); 709 contextUsageTypeMap.put("zone-short", CapitalizationContextUsage.ZONE_SHORT); 710 contextUsageTypeMap.put("metazone-long", CapitalizationContextUsage.METAZONE_LONG); 711 contextUsageTypeMap.put("metazone-short", CapitalizationContextUsage.METAZONE_SHORT); 712 } 713 714 /** 715 * Capitalization transforms. For each usage type, the first array element indicates 716 * whether to titlecase for uiListOrMenu context, the second indicates whether to 717 * titlecase for stand-alone context. 718 * @serial 719 */ 720 Map<CapitalizationContextUsage,boolean[]> capitalization = null; 721 722 /** 723 * Returns era strings. For example: "AD" and "BC". 724 * @return the era strings. 725 * @stable ICU 2.0 726 */ 727 public String[] getEras() { 728 return duplicate(eras); 729 } 730 731 /** 732 * Sets era strings. For example: "AD" and "BC". 733 * @param newEras the new era strings. 734 * @stable ICU 2.0 735 */ 736 public void setEras(String[] newEras) { 737 eras = duplicate(newEras); 738 } 739 740 /** 741 * {@icu} Returns era name strings. For example: "Anno Domini" and "Before Christ". 742 * @return the era strings. 743 * @stable ICU 3.4 744 */ 745 public String[] getEraNames() { 746 return duplicate(eraNames); 747 } 748 749 /** 750 * {@icu} Sets era name strings. For example: "Anno Domini" and "Before Christ". 751 * @param newEraNames the new era strings. 752 * @stable ICU 3.8 753 */ 754 public void setEraNames(String[] newEraNames) { 755 eraNames = duplicate(newEraNames); 756 } 757 758 // Android patch (http://b/30464240) start: Add getter for narrow eras. 759 /** 760 * {@icu} Returns narrow era name strings. For example: "A" and "B". 761 * @return the era strings. 762 * @internal 763 * @deprecated This API is ICU internal only. 764 */ 765 @Deprecated 766 public String[] getNarrowEras() { 767 return duplicate(narrowEras); 768 } 769 // Android patch end. 770 771 /** 772 * Returns month strings. For example: "January", "February", etc. 773 * @return the month strings. 774 * @stable ICU 2.0 775 */ 776 public String[] getMonths() { 777 return duplicate(months); 778 } 779 780 /** 781 * Returns month strings. For example: "January", "February", etc. 782 * @param context The month context, FORMAT or STANDALONE. 783 * @param width The width or the returned month string, 784 * either WIDE, ABBREVIATED, or NARROW. 785 * @return the month strings. 786 * @stable ICU 3.4 787 */ 788 public String[] getMonths(int context, int width) { 789 String [] returnValue = null; 790 switch (context) { 791 case FORMAT : 792 switch(width) { 793 case WIDE : 794 returnValue = months; 795 break; 796 case ABBREVIATED : 797 case SHORT : // no month data for this, defaults to ABBREVIATED 798 returnValue = shortMonths; 799 break; 800 case NARROW : 801 returnValue = narrowMonths; 802 break; 803 } 804 break; 805 case STANDALONE : 806 switch(width) { 807 case WIDE : 808 returnValue = standaloneMonths; 809 break; 810 case ABBREVIATED : 811 case SHORT : // no month data for this, defaults to ABBREVIATED 812 returnValue = standaloneShortMonths; 813 break; 814 case NARROW : 815 returnValue = standaloneNarrowMonths; 816 break; 817 } 818 break; 819 } 820 if (returnValue == null) { 821 throw new IllegalArgumentException("Bad context or width argument"); 822 } 823 return duplicate(returnValue); 824 } 825 826 /** 827 * Sets month strings. For example: "January", "February", etc. 828 * @param newMonths the new month strings. 829 * @stable ICU 2.0 830 */ 831 public void setMonths(String[] newMonths) { 832 months = duplicate(newMonths); 833 } 834 835 /** 836 * Sets month strings. For example: "January", "February", etc. 837 * @param newMonths the new month strings. 838 * @param context The formatting context, FORMAT or STANDALONE. 839 * @param width The width of the month string, 840 * either WIDE, ABBREVIATED, or NARROW. 841 * @stable ICU 3.8 842 */ 843 public void setMonths(String[] newMonths, int context, int width) { 844 switch (context) { 845 case FORMAT : 846 switch(width) { 847 case WIDE : 848 months = duplicate(newMonths); 849 break; 850 case ABBREVIATED : 851 shortMonths = duplicate(newMonths); 852 break; 853 case NARROW : 854 narrowMonths = duplicate(newMonths); 855 break; 856 default : // HANDLE SHORT, etc. 857 break; 858 } 859 break; 860 case STANDALONE : 861 switch(width) { 862 case WIDE : 863 standaloneMonths = duplicate(newMonths); 864 break; 865 case ABBREVIATED : 866 standaloneShortMonths = duplicate(newMonths); 867 break; 868 case NARROW : 869 standaloneNarrowMonths = duplicate(newMonths); 870 break; 871 default : // HANDLE SHORT, etc. 872 break; 873 } 874 break; 875 } 876 } 877 878 /** 879 * Returns short month strings. For example: "Jan", "Feb", etc. 880 * @return the short month strings. 881 * @stable ICU 2.0 882 */ 883 public String[] getShortMonths() { 884 return duplicate(shortMonths); 885 } 886 887 /** 888 * Sets short month strings. For example: "Jan", "Feb", etc. 889 * @param newShortMonths the new short month strings. 890 * @stable ICU 2.0 891 */ 892 public void setShortMonths(String[] newShortMonths) { 893 shortMonths = duplicate(newShortMonths); 894 } 895 896 /** 897 * Returns wide weekday strings. For example: "Sunday", "Monday", etc. 898 * @return the weekday strings. Use <code>Calendar.SUNDAY</code>, 899 * <code>Calendar.MONDAY</code>, etc. to index the result array. 900 * @stable ICU 2.0 901 */ 902 public String[] getWeekdays() { 903 return duplicate(weekdays); 904 } 905 906 /** 907 * Returns weekday strings. For example: "Sunday", "Monday", etc. 908 * @return the weekday strings. Use <code>Calendar.SUNDAY</code>, 909 * <code>Calendar.MONDAY</code>, etc. to index the result array. 910 * @param context Formatting context, either FORMAT or STANDALONE. 911 * @param width Width of strings to be returned, either 912 * WIDE, ABBREVIATED, SHORT, or NARROW 913 * @stable ICU 3.4 914 */ 915 public String[] getWeekdays(int context, int width) { 916 String [] returnValue = null; 917 switch (context) { 918 case FORMAT : 919 switch(width) { 920 case WIDE : 921 returnValue = weekdays; 922 break; 923 case ABBREVIATED : 924 returnValue = shortWeekdays; 925 break; 926 case SHORT : 927 returnValue = (shorterWeekdays != null)? shorterWeekdays: shortWeekdays; 928 break; 929 case NARROW : 930 returnValue = narrowWeekdays; 931 break; 932 } 933 break; 934 case STANDALONE : 935 switch(width) { 936 case WIDE : 937 returnValue = standaloneWeekdays; 938 break; 939 case ABBREVIATED : 940 returnValue = standaloneShortWeekdays; 941 break; 942 case SHORT : 943 returnValue = (standaloneShorterWeekdays != null)? standaloneShorterWeekdays: standaloneShortWeekdays; 944 break; 945 case NARROW : 946 returnValue = standaloneNarrowWeekdays; 947 break; 948 } 949 break; 950 } 951 if (returnValue == null) { 952 throw new IllegalArgumentException("Bad context or width argument"); 953 } 954 return duplicate(returnValue); 955 } 956 957 /** 958 * Sets weekday strings. For example: "Sunday", "Monday", etc. 959 * @param newWeekdays The new weekday strings. 960 * @param context The formatting context, FORMAT or STANDALONE. 961 * @param width The width of the strings, 962 * either WIDE, ABBREVIATED, SHORT, or NARROW. 963 * @stable ICU 3.8 964 */ 965 public void setWeekdays(String[] newWeekdays, int context, int width) { 966 switch (context) { 967 case FORMAT : 968 switch(width) { 969 case WIDE : 970 weekdays = duplicate(newWeekdays); 971 break; 972 case ABBREVIATED : 973 shortWeekdays = duplicate(newWeekdays); 974 break; 975 case SHORT : 976 shorterWeekdays = duplicate(newWeekdays); 977 break; 978 case NARROW : 979 narrowWeekdays = duplicate(newWeekdays); 980 break; 981 } 982 break; 983 case STANDALONE : 984 switch(width) { 985 case WIDE : 986 standaloneWeekdays = duplicate(newWeekdays); 987 break; 988 case ABBREVIATED : 989 standaloneShortWeekdays = duplicate(newWeekdays); 990 break; 991 case SHORT : 992 standaloneShorterWeekdays = duplicate(newWeekdays); 993 break; 994 case NARROW : 995 standaloneNarrowWeekdays = duplicate(newWeekdays); 996 break; 997 } 998 break; 999 } 1000 } 1001 1002 /** 1003 * Sets wide weekday strings. For example: "Sunday", "Monday", etc. 1004 * @param newWeekdays the new weekday strings. The array should 1005 * be indexed by <code>Calendar.SUNDAY</code>, 1006 * <code>Calendar.MONDAY</code>, etc. 1007 * @stable ICU 2.0 1008 */ 1009 public void setWeekdays(String[] newWeekdays) { 1010 weekdays = duplicate(newWeekdays); 1011 } 1012 1013 /** 1014 * Returns abbreviated weekday strings; for example: "Sun", "Mon", etc. 1015 * (Note: the method name is misleading; it does not get the CLDR-style 1016 * "short" weekday strings, e.g. "Su", "Mo", etc.) 1017 * @return the abbreviated weekday strings. Use <code>Calendar.SUNDAY</code>, 1018 * <code>Calendar.MONDAY</code>, etc. to index the result array. 1019 * @stable ICU 2.0 1020 */ 1021 public String[] getShortWeekdays() { 1022 return duplicate(shortWeekdays); 1023 } 1024 1025 /** 1026 * Sets abbreviated weekday strings; for example: "Sun", "Mon", etc. 1027 * (Note: the method name is misleading; it does not set the CLDR-style 1028 * "short" weekday strings, e.g. "Su", "Mo", etc.) 1029 * @param newAbbrevWeekdays the new abbreviated weekday strings. The array should 1030 * be indexed by <code>Calendar.SUNDAY</code>, 1031 * <code>Calendar.MONDAY</code>, etc. 1032 * @stable ICU 2.0 1033 */ 1034 public void setShortWeekdays(String[] newAbbrevWeekdays) { 1035 shortWeekdays = duplicate(newAbbrevWeekdays); 1036 } 1037 /** 1038 * {@icu} Returns quarter strings. For example: "1st Quarter", "2nd Quarter", etc. 1039 * @param context The quarter context, FORMAT or STANDALONE. 1040 * @param width The width or the returned quarter string, 1041 * either WIDE or ABBREVIATED. There are no NARROW quarters. 1042 * @return the quarter strings. 1043 * @stable ICU 3.6 1044 */ 1045 public String[] getQuarters(int context, int width) { 1046 String [] returnValue = null; 1047 switch (context) { 1048 case FORMAT : 1049 switch(width) { 1050 case WIDE : 1051 returnValue = quarters; 1052 break; 1053 case ABBREVIATED : 1054 case SHORT : // no quarter data for this, defaults to ABBREVIATED 1055 returnValue = shortQuarters; 1056 break; 1057 case NARROW : 1058 returnValue = null; 1059 break; 1060 } 1061 break; 1062 1063 case STANDALONE : 1064 switch(width) { 1065 case WIDE : 1066 returnValue = standaloneQuarters; 1067 break; 1068 case ABBREVIATED : 1069 case SHORT : // no quarter data for this, defaults to ABBREVIATED 1070 returnValue = standaloneShortQuarters; 1071 break; 1072 case NARROW: 1073 returnValue = null; 1074 break; 1075 } 1076 break; 1077 } 1078 if (returnValue == null) { 1079 throw new IllegalArgumentException("Bad context or width argument"); 1080 } 1081 return duplicate(returnValue); 1082 } 1083 1084 /** 1085 * {@icu} Sets quarter strings. For example: "1st Quarter", "2nd Quarter", etc. 1086 * @param newQuarters the new quarter strings. 1087 * @param context The formatting context, FORMAT or STANDALONE. 1088 * @param width The width of the quarter string, 1089 * either WIDE or ABBREVIATED. There are no NARROW quarters. 1090 * @stable ICU 3.8 1091 */ 1092 public void setQuarters(String[] newQuarters, int context, int width) { 1093 switch (context) { 1094 case FORMAT : 1095 switch(width) { 1096 case WIDE : 1097 quarters = duplicate(newQuarters); 1098 break; 1099 case ABBREVIATED : 1100 shortQuarters = duplicate(newQuarters); 1101 break; 1102 case NARROW : 1103 //narrowQuarters = duplicate(newQuarters); 1104 break; 1105 default : // HANDLE SHORT, etc. 1106 break; 1107 } 1108 break; 1109 case STANDALONE : 1110 switch(width) { 1111 case WIDE : 1112 standaloneQuarters = duplicate(newQuarters); 1113 break; 1114 case ABBREVIATED : 1115 standaloneShortQuarters = duplicate(newQuarters); 1116 break; 1117 case NARROW : 1118 //standaloneNarrowQuarters = duplicate(newQuarters); 1119 break; 1120 default : // HANDLE SHORT, etc. 1121 break; 1122 } 1123 break; 1124 } 1125 } 1126 1127 /** 1128 * Returns cyclic year name strings if the calendar has them, 1129 * for example: "jia-zi", "yi-chou", etc. 1130 * @param context The usage context: FORMAT, STANDALONE. 1131 * @param width The requested name width: WIDE, ABBREVIATED, SHORT, NARROW. 1132 * @return The year name strings, or null if they are not 1133 * available for this calendar. 1134 * @stable ICU 54 1135 */ 1136 public String[] getYearNames(int context, int width) { 1137 // context & width ignored for now, one set of names for all uses 1138 if (shortYearNames != null) { 1139 return duplicate(shortYearNames); 1140 } 1141 return null; 1142 } 1143 1144 /** 1145 * Sets cyclic year name strings, for example: "jia-zi", "yi-chou", etc. 1146 * @param yearNames The new cyclic year name strings. 1147 * @param context The usage context: FORMAT, STANDALONE (currently only FORMAT is supported). 1148 * @param width The name width: WIDE, ABBREVIATED, NARROW (currently only ABBREVIATED is supported). 1149 * @stable ICU 54 1150 */ 1151 public void setYearNames(String[] yearNames, int context, int width) { 1152 if (context == FORMAT && width == ABBREVIATED) { 1153 shortYearNames = duplicate(yearNames); 1154 } 1155 } 1156 1157 /** 1158 * Returns calendar zodiac name strings if the calendar has them, 1159 * for example: "Rat", "Ox", "Tiger", etc. 1160 * @param context The usage context: FORMAT, STANDALONE. 1161 * @param width The requested name width: WIDE, ABBREVIATED, SHORT, NARROW. 1162 * @return The zodiac name strings, or null if they are not 1163 * available for this calendar. 1164 * @stable ICU 54 1165 */ 1166 public String[] getZodiacNames(int context, int width) { 1167 // context & width ignored for now, one set of names for all uses 1168 if (shortZodiacNames != null) { 1169 return duplicate(shortZodiacNames); 1170 } 1171 return null; 1172 } 1173 1174 /** 1175 * Sets calendar zodiac name strings, for example: "Rat", "Ox", "Tiger", etc. 1176 * @param zodiacNames The new zodiac name strings. 1177 * @param context The usage context: FORMAT, STANDALONE (currently only FORMAT is supported). 1178 * @param width The name width: WIDE, ABBREVIATED, NARROW (currently only ABBREVIATED is supported). 1179 * @stable ICU 54 1180 */ 1181 public void setZodiacNames(String[] zodiacNames, int context, int width) { 1182 if (context == FORMAT && width == ABBREVIATED) { 1183 shortZodiacNames = duplicate(zodiacNames); 1184 } 1185 } 1186 1187 /** 1188 * Returns the appropriate leapMonthPattern if the calendar has them, 1189 * for example: "{0}bis" 1190 * @param context The usage context: FORMAT, STANDALONE, NUMERIC. 1191 * @param width The requested pattern width: WIDE, ABBREVIATED, SHORT, NARROW. 1192 * @return The leapMonthPattern, or null if not available for 1193 * this calendar. 1194 * @internal 1195 * @deprecated This API is ICU internal only. 1196 */ 1197 @Deprecated 1198 public String getLeapMonthPattern(int context, int width) { 1199 if (leapMonthPatterns != null) { 1200 int leapMonthPatternIndex = -1; 1201 switch (context) { 1202 case FORMAT : 1203 switch(width) { 1204 case WIDE : 1205 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_WIDE; 1206 break; 1207 case ABBREVIATED : 1208 case SHORT : // no month data for this, defaults to ABBREVIATED 1209 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV; 1210 break; 1211 case NARROW : 1212 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_NARROW; 1213 break; 1214 } 1215 break; 1216 case STANDALONE : 1217 switch(width) { 1218 case WIDE : 1219 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE; 1220 break; 1221 case ABBREVIATED : 1222 case SHORT : // no month data for this, defaults to ABBREVIATED 1223 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV; 1224 break; 1225 case NARROW : 1226 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW; 1227 break; 1228 } 1229 break; 1230 case NUMERIC : 1231 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_NUMERIC; 1232 break; 1233 } 1234 if (leapMonthPatternIndex < 0) { 1235 throw new IllegalArgumentException("Bad context or width argument"); 1236 } 1237 return leapMonthPatterns[leapMonthPatternIndex]; 1238 } 1239 return null; 1240 } 1241 1242 /** 1243 * Sets a leapMonthPattern, for example: "{0}bis" 1244 * @param leapMonthPattern The new leapMonthPattern. 1245 * @param context The usage context: FORMAT, STANDALONE, NUMERIC. 1246 * @param width The name width: WIDE, ABBREVIATED, NARROW. 1247 * @internal 1248 * @deprecated This API is ICU internal only. 1249 */ 1250 @Deprecated 1251 public void setLeapMonthPattern(String leapMonthPattern, int context, int width) { 1252 if (leapMonthPatterns != null) { 1253 int leapMonthPatternIndex = -1; 1254 switch (context) { 1255 case FORMAT : 1256 switch(width) { 1257 case WIDE : 1258 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_WIDE; 1259 break; 1260 case ABBREVIATED : 1261 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV; 1262 break; 1263 case NARROW : 1264 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_NARROW; 1265 break; 1266 default : // HANDLE SHORT, etc. 1267 break; 1268 } 1269 break; 1270 case STANDALONE : 1271 switch(width) { 1272 case WIDE : 1273 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE; 1274 break; 1275 case ABBREVIATED : 1276 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV; 1277 break; 1278 case NARROW : 1279 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW; 1280 break; 1281 default : // HANDLE SHORT, etc. 1282 break; 1283 } 1284 break; 1285 case NUMERIC : 1286 leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_NUMERIC; 1287 break; 1288 default : 1289 break; 1290 } 1291 if (leapMonthPatternIndex >= 0) { 1292 leapMonthPatterns[leapMonthPatternIndex] = leapMonthPattern; 1293 } 1294 } 1295 } 1296 1297 /** 1298 * Returns am/pm strings. For example: "AM" and "PM". 1299 * @return the weekday strings. 1300 * @stable ICU 2.0 1301 */ 1302 public String[] getAmPmStrings() { 1303 return duplicate(ampms); 1304 } 1305 1306 /** 1307 * Sets am/pm strings. For example: "AM" and "PM". 1308 * @param newAmpms the new ampm strings. 1309 * @stable ICU 2.0 1310 */ 1311 public void setAmPmStrings(String[] newAmpms) { 1312 ampms = duplicate(newAmpms); 1313 } 1314 1315 /** 1316 * Returns the time separator string. For example: ":". 1317 * @return the time separator string. 1318 * @internal 1319 * @deprecated This API is ICU internal only. 1320 */ 1321 @Deprecated 1322 public String getTimeSeparatorString() { 1323 return timeSeparator; 1324 } 1325 1326 /** 1327 * Sets the time separator string. For example: ":". 1328 * @param newTimeSeparator the new time separator string. 1329 * @internal 1330 * @deprecated This API is ICU internal only. 1331 */ 1332 @Deprecated 1333 public void setTimeSeparatorString(String newTimeSeparator) { 1334 timeSeparator = newTimeSeparator; 1335 } 1336 1337 /** 1338 * Returns time zone strings. 1339 * <p> 1340 * The array returned by this API is a two dimensional String array and 1341 * each row contains at least following strings: 1342 * <ul> 1343 * <li>ZoneStrings[n][0] - System time zone ID 1344 * <li>ZoneStrings[n][1] - Long standard time display name 1345 * <li>ZoneStrings[n][2] - Short standard time display name 1346 * <li>ZoneStrings[n][3] - Long daylight saving time display name 1347 * <li>ZoneStrings[n][4] - Short daylight saving time display name 1348 * </ul> 1349 * When a localized display name is not available, the corresponding 1350 * array element will be <code>null</code>. 1351 * <p> 1352 * <b>Note</b>: ICU implements the time zone display name formatting algorithm 1353 * specified by <a href="http://www.unicode.org/reports/tr35/">UTS#35 Unicode 1354 * Locale Data Markup Language(LDML)</a>. The algorithm supports historic 1355 * display name changes and various different types of names not available in 1356 * {@link java.text.DateFormatSymbols#getZoneStrings()}. For accessing the full 1357 * set of time zone string data used by ICU implementation, you should use 1358 * {@link TimeZoneNames} APIs instead. 1359 * 1360 * @return the time zone strings. 1361 * @stable ICU 2.0 1362 */ 1363 public String[][] getZoneStrings() { 1364 if (zoneStrings != null) { 1365 return duplicate(zoneStrings); 1366 } 1367 1368 String[] tzIDs = TimeZone.getAvailableIDs(); 1369 TimeZoneNames tznames = TimeZoneNames.getInstance(validLocale); 1370 tznames.loadAllDisplayNames(); 1371 NameType types[] = { 1372 NameType.LONG_STANDARD, NameType.SHORT_STANDARD, 1373 NameType.LONG_DAYLIGHT, NameType.SHORT_DAYLIGHT 1374 }; 1375 long now = System.currentTimeMillis(); 1376 String[][] array = new String[tzIDs.length][5]; 1377 for (int i = 0; i < tzIDs.length; i++) { 1378 String canonicalID = TimeZone.getCanonicalID(tzIDs[i]); 1379 if (canonicalID == null) { 1380 canonicalID = tzIDs[i]; 1381 } 1382 1383 array[i][0] = tzIDs[i]; 1384 tznames.getDisplayNames(canonicalID, types, now, array[i], 1); 1385 } 1386 1387 zoneStrings = array; 1388 return zoneStrings; 1389 } 1390 1391 /** 1392 * Sets time zone strings. 1393 * <p> 1394 * <b>Note</b>: {@link SimpleDateFormat} no longer uses the 1395 * zone strings stored in a <code>DateFormatSymbols</code>. 1396 * Therefore, the time zone strings set by this method have 1397 * no effects in an instance of <code>SimpleDateFormat</code> 1398 * for formatting time zones. If you want to customize time 1399 * zone display names formatted by <code>SimpleDateFormat</code>, 1400 * you should customize {@link TimeZoneFormat} and set the 1401 * instance by {@link SimpleDateFormat#setTimeZoneFormat(TimeZoneFormat)} 1402 * instead. 1403 * 1404 * @param newZoneStrings the new time zone strings. 1405 * @stable ICU 2.0 1406 */ 1407 public void setZoneStrings(String[][] newZoneStrings) { 1408 zoneStrings = duplicate(newZoneStrings); 1409 } 1410 1411 /** 1412 * Returns localized date-time pattern characters. For example: 'u', 't', etc. 1413 * 1414 * <p>Note: ICU no longer provides localized date-time pattern characters for a locale 1415 * starting ICU 3.8. This method returns the non-localized date-time pattern 1416 * characters unless user defined localized data is set by setLocalPatternChars. 1417 * @return the localized date-time pattern characters. 1418 * @stable ICU 2.0 1419 */ 1420 public String getLocalPatternChars() { 1421 return localPatternChars; 1422 } 1423 1424 /** 1425 * Sets localized date-time pattern characters. For example: 'u', 't', etc. 1426 * @param newLocalPatternChars the new localized date-time 1427 * pattern characters. 1428 * @stable ICU 2.0 1429 */ 1430 public void setLocalPatternChars(String newLocalPatternChars) { 1431 localPatternChars = newLocalPatternChars; 1432 } 1433 1434 /** 1435 * Overrides clone. 1436 * @stable ICU 2.0 1437 */ 1438 @Override 1439 public Object clone() 1440 { 1441 try { 1442 DateFormatSymbols other = (DateFormatSymbols)super.clone(); 1443 return other; 1444 } catch (CloneNotSupportedException e) { 1445 ///CLOVER:OFF 1446 throw new ICUCloneNotSupportedException(e); 1447 ///CLOVER:ON 1448 } 1449 } 1450 1451 /** 1452 * Override hashCode. 1453 * Generates a hash code for the DateFormatSymbols object. 1454 * @stable ICU 2.0 1455 */ 1456 @Override 1457 public int hashCode() { 1458 // Is this sufficient? 1459 return requestedLocale.toString().hashCode(); 1460 } 1461 1462 /** 1463 * Overrides equals. 1464 * @stable ICU 2.0 1465 */ 1466 @Override 1467 public boolean equals(Object obj) 1468 { 1469 if (this == obj) return true; 1470 if (obj == null || getClass() != obj.getClass()) return false; 1471 DateFormatSymbols that = (DateFormatSymbols) obj; 1472 return (Utility.arrayEquals(eras, that.eras) 1473 && Utility.arrayEquals(eraNames, that.eraNames) 1474 && Utility.arrayEquals(months, that.months) 1475 && Utility.arrayEquals(shortMonths, that.shortMonths) 1476 && Utility.arrayEquals(narrowMonths, that.narrowMonths) 1477 && Utility.arrayEquals(standaloneMonths, that.standaloneMonths) 1478 && Utility.arrayEquals(standaloneShortMonths, that.standaloneShortMonths) 1479 && Utility.arrayEquals(standaloneNarrowMonths, that.standaloneNarrowMonths) 1480 && Utility.arrayEquals(weekdays, that.weekdays) 1481 && Utility.arrayEquals(shortWeekdays, that.shortWeekdays) 1482 && Utility.arrayEquals(shorterWeekdays, that.shorterWeekdays) 1483 && Utility.arrayEquals(narrowWeekdays, that.narrowWeekdays) 1484 && Utility.arrayEquals(standaloneWeekdays, that.standaloneWeekdays) 1485 && Utility.arrayEquals(standaloneShortWeekdays, that.standaloneShortWeekdays) 1486 && Utility.arrayEquals(standaloneShorterWeekdays, that.standaloneShorterWeekdays) 1487 && Utility.arrayEquals(standaloneNarrowWeekdays, that.standaloneNarrowWeekdays) 1488 && Utility.arrayEquals(ampms, that.ampms) 1489 && Utility.arrayEquals(ampmsNarrow, that.ampmsNarrow) 1490 && Utility.arrayEquals(abbreviatedDayPeriods, that.abbreviatedDayPeriods) 1491 && Utility.arrayEquals(wideDayPeriods, that.wideDayPeriods) 1492 && Utility.arrayEquals(narrowDayPeriods, that.narrowDayPeriods) 1493 && Utility.arrayEquals(standaloneAbbreviatedDayPeriods, that.standaloneAbbreviatedDayPeriods) 1494 && Utility.arrayEquals(standaloneWideDayPeriods, that.standaloneWideDayPeriods) 1495 && Utility.arrayEquals(standaloneNarrowDayPeriods, that.standaloneNarrowDayPeriods) 1496 && Utility.arrayEquals(timeSeparator, that.timeSeparator) 1497 && arrayOfArrayEquals(zoneStrings, that.zoneStrings) 1498 // getDiplayName maps deprecated country and language codes to the current ones 1499 // too bad there is no way to get the current codes! 1500 // I thought canolicalize() would map the codes but .. alas! it doesn't. 1501 && requestedLocale.getDisplayName().equals(that.requestedLocale.getDisplayName()) 1502 && Utility.arrayEquals(localPatternChars, 1503 that.localPatternChars)); 1504 } 1505 1506 // =======================privates=============================== 1507 1508 /** 1509 * Useful constant for defining timezone offsets. 1510 */ 1511 static final int millisPerHour = 60*60*1000; 1512 1513 // DateFormatSymbols cache 1514 private static CacheBase<String, DateFormatSymbols, ULocale> DFSCACHE = 1515 new SoftCache<String, DateFormatSymbols, ULocale>() { 1516 @Override 1517 protected DateFormatSymbols createInstance(String key, ULocale locale) { 1518 // Extract the type string from the key. 1519 // Otherwise we would have to create a pair object that 1520 // carries both the locale and the type. 1521 int typeStart = key.indexOf('+') + 1; 1522 int typeLimit = key.indexOf('+', typeStart); 1523 if (typeLimit < 0) { 1524 // no numbers keyword value 1525 typeLimit = key.length(); 1526 } 1527 String type = key.substring(typeStart, typeLimit); 1528 return new DateFormatSymbols(locale, null, type); 1529 } 1530 }; 1531 1532 /** 1533 * Initializes format symbols for the locale and calendar type 1534 * @param desiredLocale The locale whose symbols are desired. 1535 * @param type The calendar type whose date format symbols are desired. 1536 * @stable ICU 3.0 1537 */ 1538 //TODO: This protected seems to be marked as @stable accidentally. 1539 // We may need to deescalate this API to @internal. 1540 protected void initializeData(ULocale desiredLocale, String type) 1541 { 1542 String key = desiredLocale.getBaseName() + '+' + type; 1543 String ns = desiredLocale.getKeywordValue("numbers"); 1544 if (ns != null && ns.length() > 0) { 1545 key += '+' + ns; 1546 } 1547 DateFormatSymbols dfs = DFSCACHE.getInstance(key, desiredLocale); 1548 initializeData(dfs); 1549 } 1550 1551 /** 1552 * Initializes format symbols using another instance. 1553 * 1554 * TODO Clean up initialization methods for subclasses 1555 */ 1556 void initializeData(DateFormatSymbols dfs) { 1557 this.eras = dfs.eras; 1558 this.eraNames = dfs.eraNames; 1559 this.narrowEras = dfs.narrowEras; 1560 this.months = dfs.months; 1561 this.shortMonths = dfs.shortMonths; 1562 this.narrowMonths = dfs.narrowMonths; 1563 this.standaloneMonths = dfs.standaloneMonths; 1564 this.standaloneShortMonths = dfs.standaloneShortMonths; 1565 this.standaloneNarrowMonths = dfs.standaloneNarrowMonths; 1566 this.weekdays = dfs.weekdays; 1567 this.shortWeekdays = dfs.shortWeekdays; 1568 this.shorterWeekdays = dfs.shorterWeekdays; 1569 this.narrowWeekdays = dfs.narrowWeekdays; 1570 this.standaloneWeekdays = dfs.standaloneWeekdays; 1571 this.standaloneShortWeekdays = dfs.standaloneShortWeekdays; 1572 this.standaloneShorterWeekdays = dfs.standaloneShorterWeekdays; 1573 this.standaloneNarrowWeekdays = dfs.standaloneNarrowWeekdays; 1574 this.ampms = dfs.ampms; 1575 this.ampmsNarrow = dfs.ampmsNarrow; 1576 this.timeSeparator = dfs.timeSeparator; 1577 this.shortQuarters = dfs.shortQuarters; 1578 this.quarters = dfs.quarters; 1579 this.standaloneShortQuarters = dfs.standaloneShortQuarters; 1580 this.standaloneQuarters = dfs.standaloneQuarters; 1581 this.leapMonthPatterns = dfs.leapMonthPatterns; 1582 this.shortYearNames = dfs.shortYearNames; 1583 this.shortZodiacNames = dfs.shortZodiacNames; 1584 this.abbreviatedDayPeriods = dfs.abbreviatedDayPeriods; 1585 this.wideDayPeriods = dfs.wideDayPeriods; 1586 this.narrowDayPeriods = dfs.narrowDayPeriods; 1587 this.standaloneAbbreviatedDayPeriods = dfs.standaloneAbbreviatedDayPeriods; 1588 this.standaloneWideDayPeriods = dfs.standaloneWideDayPeriods; 1589 this.standaloneNarrowDayPeriods = dfs.standaloneNarrowDayPeriods; 1590 1591 this.zoneStrings = dfs.zoneStrings; // always null at initialization time for now 1592 this.localPatternChars = dfs.localPatternChars; 1593 1594 this.capitalization = dfs.capitalization; 1595 1596 this.actualLocale = dfs.actualLocale; 1597 this.validLocale = dfs.validLocale; 1598 this.requestedLocale = dfs.requestedLocale; 1599 } 1600 1601 1602 /** 1603 * Sink to enumerate the calendar data 1604 */ 1605 private static final class CalendarDataSink extends UResource.Sink { 1606 1607 // Data structures to store resources from the resource bundle 1608 Map<String, String[]> arrays = new TreeMap<String, String[]>(); 1609 Map<String, Map<String, String>> maps = new TreeMap<String, Map<String, String>>(); 1610 List<String> aliasPathPairs = new ArrayList<String>(); 1611 1612 // Current and next calendar resource table which should be loaded 1613 String currentCalendarType = null; 1614 String nextCalendarType = null; 1615 1616 // Resources to visit when enumerating fallback calendars 1617 private Set<String> resourcesToVisit; 1618 1619 // Alias' relative path populated when an alias is read 1620 private String aliasRelativePath; 1621 1622 /** 1623 * Initializes CalendarDataSink with default values 1624 */ 1625 CalendarDataSink() { } 1626 1627 /** 1628 * Configure the CalendarSink to visit all the resources 1629 */ 1630 void visitAllResources() { 1631 resourcesToVisit = null; 1632 } 1633 1634 /** 1635 * Actions to be done before enumerating 1636 */ 1637 void preEnumerate(String calendarType) { 1638 currentCalendarType = calendarType; 1639 nextCalendarType = null; 1640 aliasPathPairs.clear(); 1641 } 1642 1643 @Override 1644 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 1645 assert currentCalendarType != null && !currentCalendarType.isEmpty(); 1646 1647 // Stores the resources to visit on the next calendar. 1648 Set<String> resourcesToVisitNext = null; 1649 UResource.Table calendarData = value.getTable(); 1650 1651 // Enumerate all resources for this calendar 1652 for (int i = 0; calendarData.getKeyAndValue(i, key, value); i++) { 1653 String keyString = key.toString(); 1654 1655 // == Handle aliases == 1656 AliasType aliasType = processAliasFromValue(keyString, value); 1657 if (aliasType == AliasType.GREGORIAN) { 1658 // Ignore aliases to the gregorian calendar, all of its resources will be loaded anyways. 1659 continue; 1660 1661 } else if (aliasType == AliasType.DIFFERENT_CALENDAR) { 1662 // Whenever an alias to the next calendar (except gregorian) is encountered, register the 1663 // calendar type it's pointing to 1664 if (resourcesToVisitNext == null) { 1665 resourcesToVisitNext = new HashSet<String>(); 1666 } 1667 resourcesToVisitNext.add(aliasRelativePath); 1668 continue; 1669 1670 } else if (aliasType == AliasType.SAME_CALENDAR) { 1671 // Register same-calendar alias 1672 if (!arrays.containsKey(keyString) && !maps.containsKey(keyString)) { 1673 aliasPathPairs.add(aliasRelativePath); 1674 aliasPathPairs.add(keyString); 1675 } 1676 continue; 1677 } 1678 1679 // Only visit the resources that were referenced by an alias on the previous calendar 1680 // (AmPmMarkersAbbr is an exception). 1681 if (resourcesToVisit != null && !resourcesToVisit.isEmpty() && !resourcesToVisit.contains(keyString) 1682 && !keyString.equals("AmPmMarkersAbbr")) { continue; } 1683 1684 // == Handle data == 1685 if (keyString.startsWith("AmPmMarkers")) { 1686 if (!keyString.endsWith("%variant") && !arrays.containsKey(keyString)) { 1687 String[] dataArray = value.getStringArray(); 1688 arrays.put(keyString, dataArray); 1689 } 1690 } else if (keyString.equals("eras") 1691 || keyString.equals("dayNames") 1692 || keyString.equals("monthNames") 1693 || keyString.equals("quarters") 1694 || keyString.equals("dayPeriod") 1695 || keyString.equals("monthPatterns") 1696 || keyString.equals("cyclicNameSets")) { 1697 processResource(keyString, key, value); 1698 } 1699 } 1700 1701 // Apply same-calendar aliases 1702 boolean modified; 1703 do { 1704 modified = false; 1705 for (int i = 0; i < aliasPathPairs.size();) { 1706 boolean mod = false; 1707 String alias = aliasPathPairs.get(i); 1708 if (arrays.containsKey(alias)) { 1709 arrays.put(aliasPathPairs.get(i + 1), arrays.get(alias)); 1710 mod = true; 1711 } else if (maps.containsKey(alias)) { 1712 maps.put(aliasPathPairs.get(i + 1), maps.get(alias)); 1713 mod = true; 1714 } 1715 if (mod) { 1716 aliasPathPairs.remove(i + 1); 1717 aliasPathPairs.remove(i); 1718 modified = true; 1719 } else { 1720 i += 2; 1721 } 1722 } 1723 } while (modified && !aliasPathPairs.isEmpty()); 1724 1725 // Set the resources to visit on the next calendar 1726 if (resourcesToVisitNext != null) { 1727 resourcesToVisit = resourcesToVisitNext; 1728 } 1729 } 1730 1731 /** 1732 * Process the nested resource bundle tables 1733 * @param path Table's relative path to the calendar 1734 * @param key Resource bundle key 1735 * @param value Resource bundle value (has to have the table to read) 1736 */ 1737 protected void processResource(String path, UResource.Key key, UResource.Value value) { 1738 1739 UResource.Table table = value.getTable(); 1740 Map<String, String> stringMap = null; 1741 1742 // Iterate over all the elements of the table and add them to the map 1743 for(int i = 0; table.getKeyAndValue(i, key, value); i++) { 1744 // Ignore '%variant' keys 1745 if (key.endsWith("%variant")) { continue; } 1746 1747 String keyString = key.toString(); 1748 1749 // == Handle String elements == 1750 if (value.getType() == ICUResourceBundle.STRING) { 1751 // We are on a leaf, store the map elements into the stringMap 1752 if (i == 0) { 1753 stringMap = new HashMap<String, String>(); 1754 maps.put(path, stringMap); 1755 } 1756 assert stringMap != null; 1757 stringMap.put(keyString, value.getString()); 1758 continue; 1759 } 1760 assert stringMap == null; 1761 1762 String currentPath = path + "/" + keyString; 1763 // In cyclicNameSets ignore everything but years/format/abbreviated 1764 // and zodiacs/format/abbreviated 1765 if (currentPath.startsWith("cyclicNameSets")) { 1766 if (!"cyclicNameSets/years/format/abbreviated".startsWith(currentPath) 1767 && !"cyclicNameSets/zodiacs/format/abbreviated".startsWith(currentPath) 1768 && !"cyclicNameSets/dayParts/format/abbreviated".startsWith(currentPath)) 1769 { continue; } 1770 } 1771 1772 // == Handle aliases == 1773 if (arrays.containsKey(currentPath) 1774 || maps.containsKey(currentPath)) { continue; } 1775 1776 AliasType aliasType = processAliasFromValue(currentPath, value); 1777 if (aliasType == AliasType.SAME_CALENDAR) { 1778 aliasPathPairs.add(aliasRelativePath); 1779 aliasPathPairs.add(currentPath); 1780 continue; 1781 } 1782 assert aliasType == AliasType.NONE; 1783 1784 // == Handle data == 1785 if (value.getType() == ICUResourceBundle.ARRAY) { 1786 // We are on a leaf, store the array 1787 String[] dataArray = value.getStringArray(); 1788 arrays.put(currentPath, dataArray); 1789 } else if (value.getType() == ICUResourceBundle.TABLE) { 1790 // We are not on a leaf, recursively process the subtable. 1791 processResource(currentPath, key, value); 1792 } 1793 } 1794 } 1795 1796 // Alias' path prefix 1797 private static final String CALENDAR_ALIAS_PREFIX = "/LOCALE/calendar/"; 1798 1799 /** 1800 * Populates an AliasIdentifier with the alias information contained on the UResource.Value. 1801 * @param currentRelativePath Relative path of this alias' resource 1802 * @param value Value which contains the alias 1803 * @return The AliasType of the alias found on Value 1804 */ 1805 private AliasType processAliasFromValue(String currentRelativePath, UResource.Value value) { 1806 if (value.getType() == ICUResourceBundle.ALIAS) { 1807 String aliasPath = value.getAliasString(); 1808 if (aliasPath.startsWith(CALENDAR_ALIAS_PREFIX) && 1809 aliasPath.length() > CALENDAR_ALIAS_PREFIX.length()) { 1810 int typeLimit = aliasPath.indexOf('/', CALENDAR_ALIAS_PREFIX.length()); 1811 if (typeLimit > CALENDAR_ALIAS_PREFIX.length()) { 1812 String aliasCalendarType = aliasPath.substring(CALENDAR_ALIAS_PREFIX.length(), typeLimit); 1813 aliasRelativePath = aliasPath.substring(typeLimit + 1); 1814 1815 if (currentCalendarType.equals(aliasCalendarType) 1816 && !currentRelativePath.equals(aliasRelativePath)) { 1817 // If we have an alias to the same calendar, the path to the resource must be different 1818 return AliasType.SAME_CALENDAR; 1819 1820 } else if (!currentCalendarType.equals(aliasCalendarType) 1821 && currentRelativePath.equals(aliasRelativePath)) { 1822 // If we have an alias to a different calendar, the path to the resource must be the same 1823 if (aliasCalendarType.equals("gregorian")) { 1824 return AliasType.GREGORIAN; 1825 } else if (nextCalendarType == null || nextCalendarType.equals(aliasCalendarType)) { 1826 nextCalendarType = aliasCalendarType; 1827 return AliasType.DIFFERENT_CALENDAR; 1828 } 1829 } 1830 } 1831 } 1832 throw new ICUException("Malformed 'calendar' alias. Path: " + aliasPath); 1833 } 1834 return AliasType.NONE; 1835 } 1836 1837 /** 1838 * Enum which specifies the type of alias received, or no alias 1839 */ 1840 private enum AliasType { 1841 SAME_CALENDAR, 1842 DIFFERENT_CALENDAR, 1843 GREGORIAN, 1844 NONE 1845 } 1846 } 1847 1848 /** Private, for cache.getInstance(). */ 1849 private DateFormatSymbols(ULocale desiredLocale, ICUResourceBundle b, String calendarType) { 1850 initializeData(desiredLocale, b, calendarType); 1851 } 1852 1853 /** 1854 * Initializes format symbols for the locale and calendar type 1855 * @param desiredLocale The locale whose symbols are desired. 1856 * @param b Resource bundle provided externally 1857 * @param calendarType The calendar type being used 1858 * @internal 1859 * @deprecated This API is ICU internal only. 1860 */ 1861 @Deprecated 1862 // This API was accidentally marked as @stable ICU 3.0 formerly. 1863 protected void initializeData(ULocale desiredLocale, ICUResourceBundle b, String calendarType) 1864 { 1865 // Create a CalendarSink to load this data and a resource bundle 1866 CalendarDataSink calendarSink = new CalendarDataSink(); 1867 if (b == null) { 1868 b = (ICUResourceBundle) UResourceBundle 1869 .getBundleInstance(ICUData.ICU_BASE_NAME, desiredLocale); 1870 } 1871 1872 // Iterate over the resource bundle data following the fallbacks through different calendar types 1873 while (calendarType != null) { 1874 1875 // Enumerate this calendar type. If the calendar is not found fallback to gregorian. 1876 ICUResourceBundle dataForType = b.findWithFallback("calendar/" + calendarType); 1877 if (dataForType == null) { 1878 if (!"gregorian".equals(calendarType)) { 1879 calendarType = "gregorian"; 1880 calendarSink.visitAllResources(); 1881 continue; 1882 } 1883 throw new MissingResourceException("The 'gregorian' calendar type wasn't found for the locale: " 1884 + desiredLocale.getBaseName(), getClass().getName(), "gregorian"); 1885 } 1886 calendarSink.preEnumerate(calendarType); 1887 dataForType.getAllItemsWithFallback("", calendarSink); 1888 1889 // Stop loading when gregorian was loaded 1890 if (calendarType.equals("gregorian")) { 1891 break; 1892 } 1893 1894 // Get the next calendar type to process from the sink 1895 calendarType = calendarSink.nextCalendarType; 1896 1897 // Gregorian is always the last fallback 1898 if (calendarType == null) { 1899 calendarType = "gregorian"; 1900 calendarSink.visitAllResources(); 1901 } 1902 } 1903 1904 Map<String, String[]> arrays = calendarSink.arrays; 1905 Map<String, Map<String, String>> maps = calendarSink.maps; 1906 1907 eras = arrays.get("eras/abbreviated"); 1908 eraNames = arrays.get("eras/wide"); 1909 narrowEras = arrays.get("eras/narrow"); 1910 1911 months = arrays.get("monthNames/format/wide"); 1912 shortMonths = arrays.get("monthNames/format/abbreviated"); 1913 narrowMonths = arrays.get("monthNames/format/narrow"); 1914 1915 standaloneMonths = arrays.get("monthNames/stand-alone/wide"); 1916 standaloneShortMonths = arrays.get("monthNames/stand-alone/abbreviated"); 1917 standaloneNarrowMonths = arrays.get("monthNames/stand-alone/narrow"); 1918 1919 String[] lWeekdays = arrays.get("dayNames/format/wide"); 1920 weekdays = new String[8]; 1921 weekdays[0] = ""; // 1-based 1922 System.arraycopy(lWeekdays, 0, weekdays, 1, lWeekdays.length); 1923 1924 String[] aWeekdays = arrays.get("dayNames/format/abbreviated"); 1925 shortWeekdays = new String[8]; 1926 shortWeekdays[0] = ""; // 1-based 1927 System.arraycopy(aWeekdays, 0, shortWeekdays, 1, aWeekdays.length); 1928 1929 String[] sWeekdays = arrays.get("dayNames/format/short"); 1930 shorterWeekdays = new String[8]; 1931 shorterWeekdays[0] = ""; // 1-based 1932 System.arraycopy(sWeekdays, 0, shorterWeekdays, 1, sWeekdays.length); 1933 1934 String [] nWeekdays = arrays.get("dayNames/format/narrow"); 1935 if (nWeekdays == null) { 1936 nWeekdays = arrays.get("dayNames/stand-alone/narrow"); 1937 1938 if (nWeekdays == null) { 1939 nWeekdays = arrays.get("dayNames/format/abbreviated"); 1940 1941 if (nWeekdays == null) { 1942 throw new MissingResourceException("Resource not found", 1943 getClass().getName(), "dayNames/format/abbreviated"); 1944 } 1945 } 1946 } 1947 narrowWeekdays = new String[8]; 1948 narrowWeekdays[0] = ""; // 1-based 1949 System.arraycopy(nWeekdays, 0, narrowWeekdays, 1, nWeekdays.length); 1950 1951 String [] swWeekdays = null; 1952 swWeekdays = arrays.get("dayNames/stand-alone/wide"); 1953 standaloneWeekdays = new String[8]; 1954 standaloneWeekdays[0] = ""; // 1-based 1955 System.arraycopy(swWeekdays, 0, standaloneWeekdays, 1, swWeekdays.length); 1956 1957 String [] saWeekdays = null; 1958 saWeekdays = arrays.get("dayNames/stand-alone/abbreviated"); 1959 standaloneShortWeekdays = new String[8]; 1960 standaloneShortWeekdays[0] = ""; // 1-based 1961 System.arraycopy(saWeekdays, 0, standaloneShortWeekdays, 1, saWeekdays.length); 1962 1963 String [] ssWeekdays = null; 1964 ssWeekdays = arrays.get("dayNames/stand-alone/short"); 1965 standaloneShorterWeekdays = new String[8]; 1966 standaloneShorterWeekdays[0] = ""; // 1-based 1967 System.arraycopy(ssWeekdays, 0, standaloneShorterWeekdays, 1, ssWeekdays.length); 1968 1969 String [] snWeekdays = null; 1970 snWeekdays = arrays.get("dayNames/stand-alone/narrow"); 1971 standaloneNarrowWeekdays = new String[8]; 1972 standaloneNarrowWeekdays[0] = ""; // 1-based 1973 System.arraycopy(snWeekdays, 0, standaloneNarrowWeekdays, 1, snWeekdays.length); 1974 1975 ampms = arrays.get("AmPmMarkers"); 1976 ampmsNarrow = arrays.get("AmPmMarkersNarrow"); 1977 1978 quarters = arrays.get("quarters/format/wide"); 1979 shortQuarters = arrays.get("quarters/format/abbreviated"); 1980 1981 standaloneQuarters = arrays.get("quarters/stand-alone/wide"); 1982 standaloneShortQuarters = arrays.get("quarters/stand-alone/abbreviated"); 1983 1984 abbreviatedDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/abbreviated")); 1985 wideDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/wide")); 1986 narrowDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/narrow")); 1987 standaloneAbbreviatedDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/abbreviated")); 1988 standaloneWideDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/wide")); 1989 standaloneNarrowDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/narrow")); 1990 1991 for (int i = 0; i < DT_MONTH_PATTERN_COUNT; i++) { 1992 String monthPatternPath = LEAP_MONTH_PATTERNS_PATHS[i]; 1993 if (monthPatternPath != null) { 1994 Map<String, String> monthPatternMap = maps.get(monthPatternPath); 1995 if (monthPatternMap != null) { 1996 String leapMonthPattern = monthPatternMap.get("leap"); 1997 if (leapMonthPattern != null) { 1998 if (leapMonthPatterns == null) { 1999 leapMonthPatterns = new String[DT_MONTH_PATTERN_COUNT]; 2000 } 2001 leapMonthPatterns[i] = leapMonthPattern; 2002 } 2003 } 2004 } 2005 } 2006 2007 shortYearNames = arrays.get("cyclicNameSets/years/format/abbreviated"); 2008 shortZodiacNames = arrays.get("cyclicNameSets/zodiacs/format/abbreviated"); 2009 2010 requestedLocale = desiredLocale; 2011 2012 ICUResourceBundle rb = 2013 (ICUResourceBundle)UResourceBundle.getBundleInstance( 2014 ICUData.ICU_BASE_NAME, desiredLocale); 2015 2016 localPatternChars = patternChars; 2017 2018 // TODO: obtain correct actual/valid locale later 2019 ULocale uloc = rb.getULocale(); 2020 setLocale(uloc, uloc); 2021 2022 capitalization = new HashMap<CapitalizationContextUsage,boolean[]>(); 2023 boolean[] noTransforms = new boolean[2]; 2024 noTransforms[0] = false; 2025 noTransforms[1] = false; 2026 CapitalizationContextUsage allUsages[] = CapitalizationContextUsage.values(); 2027 for (CapitalizationContextUsage usage: allUsages) { 2028 capitalization.put(usage, noTransforms); 2029 } 2030 UResourceBundle contextTransformsBundle = null; 2031 try { 2032 contextTransformsBundle = rb.getWithFallback("contextTransforms"); 2033 } 2034 catch (MissingResourceException e) { 2035 contextTransformsBundle = null; // probably redundant 2036 } 2037 if (contextTransformsBundle != null) { 2038 UResourceBundleIterator ctIterator = contextTransformsBundle.getIterator(); 2039 while ( ctIterator.hasNext() ) { 2040 UResourceBundle contextTransformUsage = ctIterator.next(); 2041 int[] intVector = contextTransformUsage.getIntVector(); 2042 if (intVector.length >= 2) { 2043 String usageKey = contextTransformUsage.getKey(); 2044 CapitalizationContextUsage usage = contextUsageTypeMap.get(usageKey); 2045 if (usage != null) { 2046 boolean[] transforms = new boolean[2]; 2047 transforms[0] = (intVector[0] != 0); 2048 transforms[1] = (intVector[1] != 0); 2049 capitalization.put(usage, transforms); 2050 } 2051 } 2052 } 2053 } 2054 2055 NumberingSystem ns = NumberingSystem.getInstance(desiredLocale); 2056 String nsName = ns == null ? "latn" : ns.getName(); // Latin is default. 2057 String tsPath = "NumberElements/" + nsName + "/symbols/timeSeparator"; 2058 try { 2059 setTimeSeparatorString(rb.getStringWithFallback(tsPath)); 2060 } catch (MissingResourceException e) { 2061 setTimeSeparatorString(DEFAULT_TIME_SEPARATOR); 2062 } 2063 } 2064 2065 /** 2066 * Resource bundle paths for each leap month pattern 2067 */ 2068 private static final String[] LEAP_MONTH_PATTERNS_PATHS = new String[DT_MONTH_PATTERN_COUNT]; 2069 static { 2070 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_WIDE] = "monthPatterns/format/wide"; 2071 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV] = "monthPatterns/format/abbreviated"; 2072 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_NARROW] = "monthPatterns/format/narrow"; 2073 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE] = "monthPatterns/stand-alone/wide"; 2074 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV] = "monthPatterns/stand-alone/abbreviated"; 2075 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW] = "monthPatterns/stand-alone/narrow"; 2076 LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_NUMERIC] = "monthPatterns/numeric/all"; 2077 } 2078 2079 private static final boolean arrayOfArrayEquals(Object[][] aa1, Object[][]aa2) { 2080 if (aa1 == aa2) { // both are null 2081 return true; 2082 } 2083 if (aa1 == null || aa2 == null) { // one is null and the other is not 2084 return false; 2085 } 2086 if (aa1.length != aa2.length) { 2087 return false; 2088 } 2089 boolean equal = true; 2090 for (int i = 0; i < aa1.length; i++) { 2091 equal = Utility.arrayEquals(aa1[i], aa2[i]); 2092 if (!equal) { 2093 break; 2094 } 2095 } 2096 return equal; 2097 } 2098 2099 /** 2100 * Keys for dayPeriods 2101 */ 2102 private static final String[] DAY_PERIOD_KEYS = {"midnight", "noon", 2103 "morning1", "afternoon1", "evening1", "night1", 2104 "morning2", "afternoon2", "evening2", "night2"}; 2105 2106 /** 2107 * Loads localized names for day periods in the requested format. 2108 * @param resourceMap Contains the dayPeriod resource to load 2109 */ 2110 private String[] loadDayPeriodStrings(Map<String, String> resourceMap) { 2111 String strings[] = new String[DAY_PERIOD_KEYS.length]; 2112 if (resourceMap != null) { 2113 for (int i = 0; i < DAY_PERIOD_KEYS.length; ++i) { 2114 strings[i] = resourceMap.get(DAY_PERIOD_KEYS[i]); // Null if string doesn't exist. 2115 } 2116 } 2117 return strings; 2118 } 2119 2120 /* 2121 * save the input locale 2122 */ 2123 private ULocale requestedLocale; 2124 2125 /* 2126 * Clones an array of Strings. 2127 * @param srcArray the source array to be cloned. 2128 * @return a cloned array. 2129 */ 2130 private final String[] duplicate(String[] srcArray) 2131 { 2132 return srcArray.clone(); 2133 } 2134 2135 private final String[][] duplicate(String[][] srcArray) 2136 { 2137 String[][] aCopy = new String[srcArray.length][]; 2138 for (int i = 0; i < srcArray.length; ++i) 2139 aCopy[i] = duplicate(srcArray[i]); 2140 return aCopy; 2141 } 2142 2143 /* 2144 * Compares the equality of the two arrays of String. 2145 * @param current this String array. 2146 * @param other that String array. 2147 private final boolean equals(String[] current, String[] other) 2148 { 2149 int count = current.length; 2150 2151 for (int i = 0; i < count; ++i) 2152 if (!current[i].equals(other[i])) 2153 return false; 2154 return true; 2155 } 2156 */ 2157 2158 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2159 2160 /** 2161 * Returns the {@link DateFormatSymbols} object that should be used to format a 2162 * calendar system's dates in the given locale. 2163 * 2164 * @param cal The calendar system whose date format symbols are desired. 2165 * @param locale The locale whose symbols are desired. 2166 * 2167 * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale) 2168 * @stable ICU 2.0 2169 */ 2170 public DateFormatSymbols(Calendar cal, Locale locale) { 2171 initializeData(ULocale.forLocale(locale), cal.getType()); 2172 } 2173 2174 /** 2175 * Returns the {@link DateFormatSymbols} object that should be used to format a 2176 * calendar system's dates in the given locale. 2177 * @param cal The calendar system whose date format symbols are desired. 2178 * @param locale The ulocale whose symbols are desired. 2179 * 2180 * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale) 2181 * @stable ICU 3.2 2182 */ 2183 public DateFormatSymbols(Calendar cal, ULocale locale) { 2184 initializeData(locale, cal.getType()); 2185 } 2186 2187 /** 2188 * Variant of DateFormatSymbols(Calendar, Locale) that takes the Calendar class 2189 * instead of a Calendar instance. 2190 * @see #DateFormatSymbols(Calendar, Locale) 2191 * @stable ICU 2.2 2192 */ 2193 public DateFormatSymbols(Class<? extends Calendar> calendarClass, Locale locale) { 2194 this(calendarClass, ULocale.forLocale(locale)); 2195 } 2196 2197 /** 2198 * Variant of DateFormatSymbols(Calendar, ULocale) that takes the Calendar class 2199 * instead of a Calendar instance. 2200 * @see #DateFormatSymbols(Calendar, Locale) 2201 * @stable ICU 3.2 2202 */ 2203 public DateFormatSymbols(Class<? extends Calendar> calendarClass, ULocale locale) { 2204 String fullName = calendarClass.getName(); 2205 int lastDot = fullName.lastIndexOf('.'); 2206 String className = fullName.substring(lastDot+1); 2207 String calType = null; 2208 for (String[] calClassInfo : CALENDAR_CLASSES) { 2209 if (calClassInfo[0].equals(className)) { 2210 calType = calClassInfo[1]; 2211 break; 2212 } 2213 } 2214 if (calType == null) { 2215 calType = className.replaceAll("Calendar", "").toLowerCase(Locale.ENGLISH); 2216 } 2217 2218 initializeData(locale, calType); 2219 } 2220 2221 // Android patch (http://b/30464240) start: Add constructor taking a calendar type. 2222 /** 2223 * Variant of DateFormatSymbols(Calendar, ULocale) that takes the calendar type 2224 * instead of a Calendar instance. 2225 * @see #DateFormatSymbols(Calendar, Locale) 2226 * @internal 2227 * @deprecated This API is ICU internal only. 2228 */ 2229 @Deprecated 2230 public DateFormatSymbols(ULocale locale, String calType) { 2231 initializeData(locale, calType); 2232 } 2233 // Android patch end. 2234 2235 /** 2236 * Fetches a custom calendar's DateFormatSymbols out of the given resource 2237 * bundle. Symbols that are not overridden are inherited from the 2238 * default DateFormatSymbols for the locale. 2239 * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale) 2240 * @stable ICU 2.0 2241 */ 2242 public DateFormatSymbols(ResourceBundle bundle, Locale locale) { 2243 this(bundle, ULocale.forLocale(locale)); 2244 } 2245 2246 /** 2247 * Fetches a custom calendar's DateFormatSymbols out of the given resource 2248 * bundle. Symbols that are not overridden are inherited from the 2249 * default DateFormatSymbols for the locale. 2250 * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale) 2251 * @stable ICU 3.2 2252 */ 2253 public DateFormatSymbols(ResourceBundle bundle, ULocale locale) { 2254 initializeData(locale, (ICUResourceBundle) bundle, CalendarUtil.getCalendarType(locale)); 2255 } 2256 2257 /** 2258 * Finds the ResourceBundle containing the date format information for 2259 * a specified calendar subclass in a given locale. 2260 * <p> 2261 * The resource bundle name is based on the calendar's fully-specified 2262 * class name, with ".resources" inserted at the end of the package name 2263 * (just before the class name) and "Symbols" appended to the end. 2264 * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar" 2265 * is "com.ibm.icu.impl.data.HebrewCalendarSymbols". 2266 * <p> 2267 * <b>Note:</b>Because of the structural changes in the ICU locale bundle, 2268 * this API no longer works as described. This method always returns null. 2269 * @deprecated ICU 4.0 2270 */ 2271 @Deprecated 2272 // This API was formerly @stable ICU 2.0 2273 static public ResourceBundle getDateFormatBundle(Class<? extends Calendar> calendarClass, 2274 Locale locale) 2275 throws MissingResourceException { 2276 return null; 2277 } 2278 2279 /** 2280 * Finds the ResourceBundle containing the date format information for 2281 * a specified calendar subclass in a given locale. 2282 * <p> 2283 * The resource bundle name is based on the calendar's fully-specified 2284 * class name, with ".resources" inserted at the end of the package name 2285 * (just before the class name) and "Symbols" appended to the end. 2286 * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar" 2287 * is "com.ibm.icu.impl.data.HebrewCalendarSymbols". 2288 * <p> 2289 * <b>Note:</b>Because of the structural changes in the ICU locale bundle, 2290 * this API no longer works as described. This method always returns null. 2291 * @deprecated ICU 4.0 2292 */ 2293 @Deprecated 2294 // This API was formerly @stable ICU 3.2 2295 static public ResourceBundle getDateFormatBundle(Class<? extends Calendar> calendarClass, 2296 ULocale locale) 2297 throws MissingResourceException { 2298 return null; 2299 } 2300 2301 /** 2302 * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes 2303 * a Calendar instance instead of a Calendar class. 2304 * <p> 2305 * <b>Note:</b>Because of the structural changes in the ICU locale bundle, 2306 * this API no longer works as described. This method always returns null. 2307 * @see #getDateFormatBundle(java.lang.Class, java.util.Locale) 2308 * @deprecated ICU 4.0 2309 */ 2310 @Deprecated 2311 // This API was formerly @stable ICU 2.2 2312 public static ResourceBundle getDateFormatBundle(Calendar cal, Locale locale) 2313 throws MissingResourceException { 2314 return null; 2315 } 2316 2317 /** 2318 * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes 2319 * a Calendar instance instead of a Calendar class. 2320 * <p> 2321 * <b>Note:</b>Because of the structural changes in the ICU locale bundle, 2322 * this API no longer works as described. This method always returns null. 2323 * @see #getDateFormatBundle(java.lang.Class, java.util.Locale) 2324 * @deprecated ICU 4.0 2325 */ 2326 @Deprecated 2327 // This API was formerly @stable ICU 3.2 2328 public static ResourceBundle getDateFormatBundle(Calendar cal, ULocale locale) 2329 throws MissingResourceException { 2330 return null; 2331 } 2332 2333 // -------- BEGIN ULocale boilerplate -------- 2334 2335 /** 2336 * Returns the locale that was used to create this object, or null. 2337 * This may may differ from the locale requested at the time of 2338 * this object's creation. For example, if an object is created 2339 * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be 2340 * drawn from <tt>en</tt> (the <i>actual</i> locale), and 2341 * <tt>en_US</tt> may be the most specific locale that exists (the 2342 * <i>valid</i> locale). 2343 * 2344 * <p>Note: This method will be implemented in ICU 3.0; ICU 2.8 2345 * contains a partial preview implementation. The * <i>actual</i> 2346 * locale is returned correctly, but the <i>valid</i> locale is 2347 * not, in most cases. 2348 * @param type type of information requested, either {@link 2349 * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link 2350 * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}. 2351 * @return the information specified by <i>type</i>, or null if 2352 * this object was not constructed from locale data. 2353 * @see com.ibm.icu.util.ULocale 2354 * @see com.ibm.icu.util.ULocale#VALID_LOCALE 2355 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE 2356 * @draft ICU 2.8 (retain) 2357 * @provisional This API might change or be removed in a future release. 2358 */ 2359 public final ULocale getLocale(ULocale.Type type) { 2360 return type == ULocale.ACTUAL_LOCALE ? 2361 this.actualLocale : this.validLocale; 2362 } 2363 2364 /** 2365 * Sets information about the locales that were used to create this 2366 * object. If the object was not constructed from locale data, 2367 * both arguments should be set to null. Otherwise, neither 2368 * should be null. The actual locale must be at the same level or 2369 * less specific than the valid locale. This method is intended 2370 * for use by factories or other entities that create objects of 2371 * this class. 2372 * @param valid the most specific locale containing any resource 2373 * data, or null 2374 * @param actual the locale containing data used to construct this 2375 * object, or null 2376 * @see com.ibm.icu.util.ULocale 2377 * @see com.ibm.icu.util.ULocale#VALID_LOCALE 2378 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE 2379 */ 2380 final void setLocale(ULocale valid, ULocale actual) { 2381 // Change the following to an assertion later 2382 if ((valid == null) != (actual == null)) { 2383 ///CLOVER:OFF 2384 throw new IllegalArgumentException(); 2385 ///CLOVER:ON 2386 } 2387 // Another check we could do is that the actual locale is at 2388 // the same level or less specific than the valid locale. 2389 this.validLocale = valid; 2390 this.actualLocale = actual; 2391 } 2392 2393 /** 2394 * The most specific locale containing any resource data, or null. 2395 * @see com.ibm.icu.util.ULocale 2396 */ 2397 private ULocale validLocale; 2398 2399 /** 2400 * The locale containing data used to construct this object, or 2401 * null. 2402 * @see com.ibm.icu.util.ULocale 2403 */ 2404 private ULocale actualLocale; 2405 2406 // -------- END ULocale boilerplate -------- 2407 2408 /** 2409 * 3.8 or older version did not have localized GMT format 2410 * patterns. 2411 */ 2412 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 2413 stream.defaultReadObject(); 2414 } 2415} 2416