1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 /* 28 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved 29 * (C) Copyright IBM Corp. 1996 - All Rights Reserved 30 * 31 * The original version of this source code and documentation is copyrighted 32 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These 33 * materials are provided under terms of a License Agreement between Taligent 34 * and Sun. This technology is protected by multiple US and International 35 * patents. This notice and attribution to Taligent may not be removed. 36 * Taligent is a registered trademark of Taligent, Inc. 37 * 38 */ 39 40 package java.text; 41 42 import java.io.IOException; 43 import java.io.ObjectInputStream; 44 import java.io.ObjectOutputStream; 45 import java.io.Serializable; 46 import java.lang.ref.SoftReference; 47 import java.util.Arrays; 48 import java.util.Locale; 49 import java.util.Objects; 50 import java.util.TimeZone; 51 import java.util.concurrent.ConcurrentHashMap; 52 import java.util.concurrent.ConcurrentMap; 53 54 import libcore.icu.ICU; 55 import libcore.icu.LocaleData; 56 import libcore.icu.TimeZoneNames; 57 58 /** 59 * <code>DateFormatSymbols</code> is a public class for encapsulating 60 * localizable date-time formatting data, such as the names of the 61 * months, the names of the days of the week, and the time zone data. 62 * <code>SimpleDateFormat</code> uses 63 * <code>DateFormatSymbols</code> to encapsulate this information. 64 * 65 * <p> 66 * Typically you shouldn't use <code>DateFormatSymbols</code> directly. 67 * Rather, you are encouraged to create a date-time formatter with the 68 * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>, 69 * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>. 70 * These methods automatically create a <code>DateFormatSymbols</code> for 71 * the formatter so that you don't have to. After the 72 * formatter is created, you may modify its format pattern using the 73 * <code>setPattern</code> method. For more information about 74 * creating formatters using <code>DateFormat</code>'s factory methods, 75 * see {@link DateFormat}. 76 * 77 * <p> 78 * If you decide to create a date-time formatter with a specific 79 * format pattern for a specific locale, you can do so with: 80 * <blockquote> 81 * <pre> 82 * new SimpleDateFormat(aPattern, DateFormatSymbols.getInstance(aLocale)). 83 * </pre> 84 * </blockquote> 85 * 86 * <p> 87 * <code>DateFormatSymbols</code> objects are cloneable. When you obtain 88 * a <code>DateFormatSymbols</code> object, feel free to modify the 89 * date-time formatting data. For instance, you can replace the localized 90 * date-time format pattern characters with the ones that you feel easy 91 * to remember. Or you can change the representative cities 92 * to your favorite ones. 93 * 94 * <p> 95 * New <code>DateFormatSymbols</code> subclasses may be added to support 96 * <code>SimpleDateFormat</code> for date-time formatting for additional locales. 97 98 * @see DateFormat 99 * @see SimpleDateFormat 100 * @see java.util.SimpleTimeZone 101 * @author Chen-Lieh Huang 102 */ 103 public class DateFormatSymbols implements Serializable, Cloneable { 104 105 // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance() 106 // be used instead in case Android supports it in future. 107 /** 108 * Construct a DateFormatSymbols object by loading format data from 109 * resources for the default {@link java.util.Locale.Category#FORMAT FORMAT} 110 * locale. It is recommended that the {@link #getInstance(Locale) getInstance} method is used 111 * instead. 112 * <p>This is equivalent to calling 113 * {@link #DateFormatSymbols(Locale) 114 * DateFormatSymbols(Locale.getDefault(Locale.Category.FORMAT))}. 115 * @see #getInstance() 116 * @see java.util.Locale#getDefault(java.util.Locale.Category) 117 * @see java.util.Locale.Category#FORMAT 118 * @exception java.util.MissingResourceException 119 * if the resources for the default locale cannot be 120 * found or cannot be loaded. 121 */ DateFormatSymbols()122 public DateFormatSymbols() 123 { 124 initializeData(Locale.getDefault(Locale.Category.FORMAT)); 125 } 126 127 // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance() 128 // be used instead in case Android supports it in future. 129 /** 130 * Construct a DateFormatSymbols object by loading format data from 131 * resources for the given locale. It is recommended that the 132 * {@link #getInstance(Locale) getInstance} method is used instead. 133 * 134 * @param locale the desired locale 135 * @see #getInstance(Locale) 136 * @exception java.util.MissingResourceException 137 * if the resources for the specified locale cannot be 138 * found or cannot be loaded. 139 */ DateFormatSymbols(Locale locale)140 public DateFormatSymbols(Locale locale) 141 { 142 initializeData(locale); 143 } 144 145 /** 146 * Era strings. For example: "AD" and "BC". An array of 2 strings, 147 * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. 148 * @serial 149 */ 150 String eras[] = null; 151 152 /** 153 * Month strings. For example: "January", "February", etc. An array 154 * of 13 strings (some calendars have 13 months), indexed by 155 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 156 * @serial 157 */ 158 String months[] = null; 159 160 /** 161 * Short month strings. For example: "Jan", "Feb", etc. An array of 162 * 13 strings (some calendars have 13 months), indexed by 163 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc. 164 165 * @serial 166 */ 167 String shortMonths[] = null; 168 169 /** 170 * Weekday strings. For example: "Sunday", "Monday", etc. An array 171 * of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 172 * <code>Calendar.MONDAY</code>, etc. 173 * The element <code>weekdays[0]</code> is ignored. 174 * @serial 175 */ 176 String weekdays[] = null; 177 178 /** 179 * Short weekday strings. For example: "Sun", "Mon", etc. An array 180 * of 8 strings, indexed by <code>Calendar.SUNDAY</code>, 181 * <code>Calendar.MONDAY</code>, etc. 182 * The element <code>shortWeekdays[0]</code> is ignored. 183 * @serial 184 */ 185 String shortWeekdays[] = null; 186 187 /** 188 * AM and PM strings. For example: "AM" and "PM". An array of 189 * 2 strings, indexed by <code>Calendar.AM</code> and 190 * <code>Calendar.PM</code>. 191 * @serial 192 */ 193 String ampms[] = null; 194 195 /** 196 * Localized names of time zones in this locale. This is a 197 * two-dimensional array of strings of size <em>n</em> by <em>m</em>, 198 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an 199 * entry containing the localized names for a single <code>TimeZone</code>. 200 * Each such row contains (with <code>i</code> ranging from 201 * 0..<em>n</em>-1): 202 * <ul> 203 * <li><code>zoneStrings[i][0]</code> - time zone ID</li> 204 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard 205 * time</li> 206 * <li><code>zoneStrings[i][2]</code> - short name of zone in 207 * standard time</li> 208 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight 209 * saving time</li> 210 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight 211 * saving time</li> 212 * </ul> 213 * The zone ID is <em>not</em> localized; it's one of the valid IDs of 214 * the {@link java.util.TimeZone TimeZone} class that are not 215 * <a href="../java/util/TimeZone.html#CustomID">custom IDs</a>. 216 * All other entries are localized names. 217 * @see java.util.TimeZone 218 * @serial 219 */ 220 String zoneStrings[][] = null; 221 222 /** 223 * Indicates that zoneStrings is set externally with setZoneStrings() method. 224 */ 225 transient boolean isZoneStringsSet = false; 226 227 /** 228 * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. 229 * All locales use the same these unlocalized pattern characters. 230 * 231 * Pretend to support 'L' and 'c' for now. It's meant for standalone weekday and 232 * month names, but we just use the non-standalone versions for now. 233 */ 234 static final String patternChars = "GyMdkHmsSEDFwWahKzZYuXLc"; 235 236 static final int PATTERN_ERA = 0; // G 237 static final int PATTERN_YEAR = 1; // y 238 static final int PATTERN_MONTH = 2; // M 239 static final int PATTERN_DAY_OF_MONTH = 3; // d 240 static final int PATTERN_HOUR_OF_DAY1 = 4; // k 241 static final int PATTERN_HOUR_OF_DAY0 = 5; // H 242 static final int PATTERN_MINUTE = 6; // m 243 static final int PATTERN_SECOND = 7; // s 244 static final int PATTERN_MILLISECOND = 8; // S 245 static final int PATTERN_DAY_OF_WEEK = 9; // E 246 static final int PATTERN_DAY_OF_YEAR = 10; // D 247 static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F 248 static final int PATTERN_WEEK_OF_YEAR = 12; // w 249 static final int PATTERN_WEEK_OF_MONTH = 13; // W 250 static final int PATTERN_AM_PM = 14; // a 251 static final int PATTERN_HOUR1 = 15; // h 252 static final int PATTERN_HOUR0 = 16; // K 253 static final int PATTERN_ZONE_NAME = 17; // z 254 static final int PATTERN_ZONE_VALUE = 18; // Z 255 static final int PATTERN_WEEK_YEAR = 19; // Y 256 static final int PATTERN_ISO_DAY_OF_WEEK = 20; // u 257 static final int PATTERN_ISO_ZONE = 21; // X 258 static final int PATTERN_MONTH_STANDALONE = 22; // L 259 static final int PATTERN_STANDALONE_DAY_OF_WEEK = 23; // c 260 261 /** 262 * Localized date-time pattern characters. For example, a locale may 263 * wish to use 'u' rather than 'y' to represent years in its date format 264 * pattern strings. 265 * This string must be exactly 18 characters long, with the index of 266 * the characters described by <code>DateFormat.ERA_FIELD</code>, 267 * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were 268 * "Xz...", then localized patterns would use 'X' for era and 'z' for year. 269 * @serial 270 */ 271 String localPatternChars = null; 272 273 /** 274 * The locale which is used for initializing this DateFormatSymbols object. 275 * 276 * @since 1.6 277 * @serial 278 */ 279 Locale locale = null; 280 281 /* use serialVersionUID from JDK 1.1.4 for interoperability */ 282 static final long serialVersionUID = -5987973545549424702L; 283 284 // the internal serial version which says which version was written 285 // - 0 (default) for version up to JDK 1.1.4 286 // - 1 Android version that contains a whole bunch of new fields. 287 static final int currentSerialVersion = 1; 288 289 /** 290 * The version of the serialized data on the stream. Possible values: 291 * <ul> 292 * <li><b>0</b> or not present on stream: JDK 1.1.4. 293 * <li><b>1</b> Android: 294 * </ul> 295 * When streaming out this class, the most recent format 296 * and the highest allowable <code>serialVersionOnStream</code> 297 * is written. 298 * @serial 299 * @since JDK1.1.4 300 */ 301 private int serialVersionOnStream = currentSerialVersion; 302 303 /** 304 * Tiny month strings; "J", "F", "M" etc. 305 * 306 * @serial 307 */ 308 private String[] tinyMonths; 309 310 /** 311 * Tiny weekday strings: "M", "F", "W" etc. 312 * 313 * @serial 314 */ 315 private String[] tinyWeekdays; 316 317 /** 318 * Standalone month strings; "January", "February", "March" etc. 319 * 320 * @serial 321 */ 322 private String[] standAloneMonths; 323 324 /** 325 * Short standalone month strings: "Jan", "Feb", "Mar" etc. 326 * 327 * @serial 328 */ 329 private String[] shortStandAloneMonths; 330 331 /** 332 * Tiny standalone month strings: "J", "F", "M" etc. 333 * 334 * @serial 335 */ 336 private String[] tinyStandAloneMonths; 337 338 /** 339 * Standalone weekday strings; "Monday", "Tuesday", "Wednesday" etc. 340 * 341 * @serial 342 */ 343 private String[] standAloneWeekdays; 344 345 /** 346 * Short standalone weekday strings; "Mon", "Tue", "Wed" etc. 347 * 348 * @serial 349 */ 350 private String[] shortStandAloneWeekdays; 351 352 /** 353 * Tiny standalone weekday strings; "M", "T", "W" etc. 354 * 355 * @serial 356 */ 357 private String[] tinyStandAloneWeekdays; 358 359 // Android-changed: Removed reference to DateFormatSymbolsProvider. 360 /** 361 * Returns an array of all locales for which the 362 * <code>getInstance</code> methods of this class can return 363 * localized instances. 364 * 365 * @return An array of locales for which localized 366 * <code>DateFormatSymbols</code> instances are available. 367 * @since 1.6 368 */ getAvailableLocales()369 public static Locale[] getAvailableLocales() { 370 // Android-changed: No support for DateFormatSymbolsProvider. 371 return ICU.getAvailableLocales(); 372 } 373 374 // Android-changed: Removed reference to DateFormatSymbolsProvider. 375 /** 376 * Gets the <code>DateFormatSymbols</code> instance for the default 377 * locale. 378 * <p>This is equivalent to calling {@link #getInstance(Locale) 379 * getInstance(Locale.getDefault(Locale.Category.FORMAT))}. 380 * @see java.util.Locale#getDefault(java.util.Locale.Category) 381 * @see java.util.Locale.Category#FORMAT 382 * @return a <code>DateFormatSymbols</code> instance. 383 * @since 1.6 384 */ getInstance()385 public static final DateFormatSymbols getInstance() { 386 return getInstance(Locale.getDefault(Locale.Category.FORMAT)); 387 } 388 389 // Android-changed: Removed reference to DateFormatSymbolsProvider. 390 /** 391 * Gets the <code>DateFormatSymbols</code> instance for the specified 392 * locale. 393 * @param locale the given locale. 394 * @return a <code>DateFormatSymbols</code> instance. 395 * @exception NullPointerException if <code>locale</code> is null 396 * @since 1.6 397 */ getInstance(Locale locale)398 public static final DateFormatSymbols getInstance(Locale locale) { 399 // Android-changed: Removed used of DateFormatSymbolsProvider. 400 return (DateFormatSymbols) getCachedInstance(locale).clone(); 401 } 402 403 /** 404 * Returns a DateFormatSymbols provided by a provider or found in 405 * the cache. Note that this method returns a cached instance, 406 * not its clone. Therefore, the instance should never be given to 407 * an application. 408 */ getInstanceRef(Locale locale)409 static final DateFormatSymbols getInstanceRef(Locale locale) { 410 // Android-changed: Removed used of DateFormatSymbolsProvider. 411 return getCachedInstance(locale); 412 } 413 414 /** 415 * Returns a cached DateFormatSymbols if it's found in the 416 * cache. Otherwise, this method returns a newly cached instance 417 * for the given locale. 418 */ getCachedInstance(Locale locale)419 private static DateFormatSymbols getCachedInstance(Locale locale) { 420 SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale); 421 DateFormatSymbols dfs = null; 422 if (ref == null || (dfs = ref.get()) == null) { 423 dfs = new DateFormatSymbols(locale); 424 ref = new SoftReference<DateFormatSymbols>(dfs); 425 SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref); 426 if (x != null) { 427 DateFormatSymbols y = x.get(); 428 if (y != null) { 429 dfs = y; 430 } else { 431 // Replace the empty SoftReference with ref. 432 cachedInstances.put(locale, ref); 433 } 434 } 435 } 436 return dfs; 437 } 438 439 /** 440 * Gets era strings. For example: "AD" and "BC". 441 * @return the era strings. 442 */ getEras()443 public String[] getEras() { 444 return Arrays.copyOf(eras, eras.length); 445 } 446 447 /** 448 * Sets era strings. For example: "AD" and "BC". 449 * @param newEras the new era strings. 450 */ setEras(String[] newEras)451 public void setEras(String[] newEras) { 452 eras = Arrays.copyOf(newEras, newEras.length); 453 cachedHashCode = 0; 454 } 455 456 /** 457 * Gets month strings. For example: "January", "February", etc. 458 * @return the month strings. 459 */ getMonths()460 public String[] getMonths() { 461 return Arrays.copyOf(months, months.length); 462 } 463 464 /** 465 * Sets month strings. For example: "January", "February", etc. 466 * @param newMonths the new month strings. 467 */ setMonths(String[] newMonths)468 public void setMonths(String[] newMonths) { 469 months = Arrays.copyOf(newMonths, newMonths.length); 470 cachedHashCode = 0; 471 } 472 473 /** 474 * Gets short month strings. For example: "Jan", "Feb", etc. 475 * @return the short month strings. 476 */ getShortMonths()477 public String[] getShortMonths() { 478 return Arrays.copyOf(shortMonths, shortMonths.length); 479 } 480 481 /** 482 * Sets short month strings. For example: "Jan", "Feb", etc. 483 * @param newShortMonths the new short month strings. 484 */ setShortMonths(String[] newShortMonths)485 public void setShortMonths(String[] newShortMonths) { 486 shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length); 487 cachedHashCode = 0; 488 } 489 490 /** 491 * Gets weekday strings. For example: "Sunday", "Monday", etc. 492 * @return the weekday strings. Use <code>Calendar.SUNDAY</code>, 493 * <code>Calendar.MONDAY</code>, etc. to index the result array. 494 */ getWeekdays()495 public String[] getWeekdays() { 496 return Arrays.copyOf(weekdays, weekdays.length); 497 } 498 499 /** 500 * Sets weekday strings. For example: "Sunday", "Monday", etc. 501 * @param newWeekdays the new weekday strings. The array should 502 * be indexed by <code>Calendar.SUNDAY</code>, 503 * <code>Calendar.MONDAY</code>, etc. 504 */ setWeekdays(String[] newWeekdays)505 public void setWeekdays(String[] newWeekdays) { 506 weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length); 507 cachedHashCode = 0; 508 } 509 510 /** 511 * Gets short weekday strings. For example: "Sun", "Mon", etc. 512 * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>, 513 * <code>Calendar.MONDAY</code>, etc. to index the result array. 514 */ getShortWeekdays()515 public String[] getShortWeekdays() { 516 return Arrays.copyOf(shortWeekdays, shortWeekdays.length); 517 } 518 519 /** 520 * Sets short weekday strings. For example: "Sun", "Mon", etc. 521 * @param newShortWeekdays the new short weekday strings. The array should 522 * be indexed by <code>Calendar.SUNDAY</code>, 523 * <code>Calendar.MONDAY</code>, etc. 524 */ setShortWeekdays(String[] newShortWeekdays)525 public void setShortWeekdays(String[] newShortWeekdays) { 526 shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length); 527 cachedHashCode = 0; 528 } 529 530 /** 531 * Gets ampm strings. For example: "AM" and "PM". 532 * @return the ampm strings. 533 */ getAmPmStrings()534 public String[] getAmPmStrings() { 535 return Arrays.copyOf(ampms, ampms.length); 536 } 537 538 /** 539 * Sets ampm strings. For example: "AM" and "PM". 540 * @param newAmpms the new ampm strings. 541 */ setAmPmStrings(String[] newAmpms)542 public void setAmPmStrings(String[] newAmpms) { 543 ampms = Arrays.copyOf(newAmpms, newAmpms.length); 544 cachedHashCode = 0; 545 } 546 547 // Android-changed: Removed reference to TimeZoneNameProvider. 548 /** 549 * Gets time zone strings. Use of this method is discouraged; use 550 * {@link java.util.TimeZone#getDisplayName() TimeZone.getDisplayName()} 551 * instead. 552 * <p> 553 * The value returned is a 554 * two-dimensional array of strings of size <em>n</em> by <em>m</em>, 555 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an 556 * entry containing the localized names for a single <code>TimeZone</code>. 557 * Each such row contains (with <code>i</code> ranging from 558 * 0..<em>n</em>-1): 559 * <ul> 560 * <li><code>zoneStrings[i][0]</code> - time zone ID</li> 561 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard 562 * time</li> 563 * <li><code>zoneStrings[i][2]</code> - short name of zone in 564 * standard time</li> 565 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight 566 * saving time</li> 567 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight 568 * saving time</li> 569 * </ul> 570 * The zone ID is <em>not</em> localized; it's one of the valid IDs of 571 * the {@link java.util.TimeZone TimeZone} class that are not 572 * <a href="../util/TimeZone.html#CustomID">custom IDs</a>. 573 * All other entries are localized names. If a zone does not implement 574 * daylight saving time, the daylight saving time names should not be used. 575 * <p> 576 * If {@link #setZoneStrings(String[][]) setZoneStrings} has been called 577 * on this <code>DateFormatSymbols</code> instance, then the strings 578 * provided by that call are returned. Otherwise, the returned array 579 * contains names provided by the runtime. 580 * 581 * @return the time zone strings. 582 * @see #setZoneStrings(String[][]) 583 */ getZoneStrings()584 public String[][] getZoneStrings() { 585 return getZoneStringsImpl(true); 586 } 587 588 /** 589 * Sets time zone strings. The argument must be a 590 * two-dimensional array of strings of size <em>n</em> by <em>m</em>, 591 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an 592 * entry containing the localized names for a single <code>TimeZone</code>. 593 * Each such row contains (with <code>i</code> ranging from 594 * 0..<em>n</em>-1): 595 * <ul> 596 * <li><code>zoneStrings[i][0]</code> - time zone ID</li> 597 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard 598 * time</li> 599 * <li><code>zoneStrings[i][2]</code> - short name of zone in 600 * standard time</li> 601 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight 602 * saving time</li> 603 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight 604 * saving time</li> 605 * </ul> 606 * The zone ID is <em>not</em> localized; it's one of the valid IDs of 607 * the {@link java.util.TimeZone TimeZone} class that are not 608 * <a href="../util/TimeZone.html#CustomID">custom IDs</a>. 609 * All other entries are localized names. 610 * 611 * @param newZoneStrings the new time zone strings. 612 * @exception IllegalArgumentException if the length of any row in 613 * <code>newZoneStrings</code> is less than 5 614 * @exception NullPointerException if <code>newZoneStrings</code> is null 615 * @see #getZoneStrings() 616 */ setZoneStrings(String[][] newZoneStrings)617 public void setZoneStrings(String[][] newZoneStrings) { 618 String[][] aCopy = new String[newZoneStrings.length][]; 619 for (int i = 0; i < newZoneStrings.length; ++i) { 620 int len = newZoneStrings[i].length; 621 if (len < 5) { 622 throw new IllegalArgumentException(); 623 } 624 aCopy[i] = Arrays.copyOf(newZoneStrings[i], len); 625 } 626 zoneStrings = aCopy; 627 isZoneStringsSet = true; 628 // Android-changed: don't include zone strings in hashCode to avoid populating it. 629 // cachedHashCode = 0; 630 } 631 632 /** 633 * Gets localized date-time pattern characters. For example: 'u', 't', etc. 634 * @return the localized date-time pattern characters. 635 */ getLocalPatternChars()636 public String getLocalPatternChars() { 637 return localPatternChars; 638 } 639 640 /** 641 * Sets localized date-time pattern characters. For example: 'u', 't', etc. 642 * @param newLocalPatternChars the new localized date-time 643 * pattern characters. 644 */ setLocalPatternChars(String newLocalPatternChars)645 public void setLocalPatternChars(String newLocalPatternChars) { 646 // Call toString() to throw an NPE in case the argument is null 647 localPatternChars = newLocalPatternChars.toString(); 648 cachedHashCode = 0; 649 } 650 getTinyMonths()651 String[] getTinyMonths() { 652 return tinyMonths; 653 } 654 getStandAloneMonths()655 String[] getStandAloneMonths() { 656 return standAloneMonths; 657 } 658 getShortStandAloneMonths()659 String[] getShortStandAloneMonths() { 660 return shortStandAloneMonths; 661 } 662 getTinyStandAloneMonths()663 String[] getTinyStandAloneMonths() { 664 return tinyStandAloneMonths; 665 } 666 getTinyWeekdays()667 String[] getTinyWeekdays() { 668 return tinyWeekdays; 669 } 670 getStandAloneWeekdays()671 String[] getStandAloneWeekdays() { 672 return standAloneWeekdays; 673 } 674 getShortStandAloneWeekdays()675 String[] getShortStandAloneWeekdays() { 676 return shortStandAloneWeekdays; 677 } 678 getTinyStandAloneWeekdays()679 String[] getTinyStandAloneWeekdays() { 680 return tinyStandAloneWeekdays; 681 } 682 683 /** 684 * Overrides Cloneable 685 */ clone()686 public Object clone() 687 { 688 try 689 { 690 DateFormatSymbols other = (DateFormatSymbols)super.clone(); 691 copyMembers(this, other); 692 return other; 693 } catch (CloneNotSupportedException e) { 694 throw new InternalError(e); 695 } 696 } 697 698 /** 699 * Override hashCode. 700 * Generates a hash code for the DateFormatSymbols object. 701 */ 702 @Override hashCode()703 public int hashCode() { 704 int hashCode = cachedHashCode; 705 if (hashCode == 0) { 706 hashCode = 5; 707 hashCode = 11 * hashCode + Arrays.hashCode(eras); 708 hashCode = 11 * hashCode + Arrays.hashCode(months); 709 hashCode = 11 * hashCode + Arrays.hashCode(shortMonths); 710 hashCode = 11 * hashCode + Arrays.hashCode(weekdays); 711 hashCode = 11 * hashCode + Arrays.hashCode(shortWeekdays); 712 hashCode = 11 * hashCode + Arrays.hashCode(ampms); 713 // Android-changed: Don't include zone strings in hashCode to avoid populating it. 714 // hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper()); 715 hashCode = 11 * hashCode + Objects.hashCode(localPatternChars); 716 cachedHashCode = hashCode; 717 } 718 719 return hashCode; 720 } 721 722 /** 723 * Override equals 724 */ equals(Object obj)725 public boolean equals(Object obj) 726 { 727 if (this == obj) return true; 728 if (obj == null || getClass() != obj.getClass()) return false; 729 DateFormatSymbols that = (DateFormatSymbols) obj; 730 if (!(Arrays.equals(eras, that.eras) 731 && Arrays.equals(months, that.months) 732 && Arrays.equals(shortMonths, that.shortMonths) 733 && Arrays.equals(tinyMonths, that.tinyMonths) 734 && Arrays.equals(weekdays, that.weekdays) 735 && Arrays.equals(shortWeekdays, that.shortWeekdays) 736 && Arrays.equals(tinyWeekdays, that.tinyWeekdays) 737 && Arrays.equals(standAloneMonths, that.standAloneMonths) 738 && Arrays.equals(shortStandAloneMonths, that.shortStandAloneMonths) 739 && Arrays.equals(tinyStandAloneMonths, that.tinyStandAloneMonths) 740 && Arrays.equals(standAloneWeekdays, that.standAloneWeekdays) 741 && Arrays.equals(shortStandAloneWeekdays, that.shortStandAloneWeekdays) 742 && Arrays.equals(tinyStandAloneWeekdays, that.tinyStandAloneWeekdays) 743 && Arrays.equals(ampms, that.ampms) 744 && ((localPatternChars != null 745 && localPatternChars.equals(that.localPatternChars)) 746 || (localPatternChars == null 747 && that.localPatternChars == null)))) { 748 return false; 749 } 750 // Android-changed: Avoid populating zoneStrings just for the comparison. 751 if (!isZoneStringsSet && !that.isZoneStringsSet && Objects.equals(locale, that.locale)) { 752 return true; 753 } 754 return Arrays.deepEquals(getZoneStringsWrapper(), that.getZoneStringsWrapper()); 755 } 756 757 // =======================privates=============================== 758 759 /** 760 * Useful constant for defining time zone offsets. 761 */ 762 static final int millisPerHour = 60*60*1000; 763 764 /** 765 * Cache to hold DateFormatSymbols instances per Locale. 766 */ 767 private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances 768 = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3); 769 770 private transient int lastZoneIndex = 0; 771 772 /** 773 * Cached hash code 774 */ 775 transient volatile int cachedHashCode = 0; 776 initializeData(Locale desiredLocale)777 private void initializeData(Locale desiredLocale) { 778 locale = desiredLocale; 779 780 // Copy values of a cached instance if any. 781 SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale); 782 DateFormatSymbols dfs; 783 if (ref != null && (dfs = ref.get()) != null) { 784 copyMembers(dfs, this); 785 return; 786 } 787 locale = LocaleData.mapInvalidAndNullLocales(locale); 788 LocaleData localeData = LocaleData.get(locale); 789 790 eras = localeData.eras; 791 792 // Month names. 793 months = localeData.longMonthNames; 794 shortMonths = localeData.shortMonthNames; 795 796 ampms = localeData.amPm; 797 localPatternChars = patternChars; 798 799 // Weekdays. 800 weekdays = localeData.longWeekdayNames; 801 shortWeekdays = localeData.shortWeekdayNames; 802 803 initializeSupplementaryData(localeData); 804 } 805 initializeSupplementaryData(LocaleData localeData)806 private void initializeSupplementaryData(LocaleData localeData) { 807 // Tiny weekdays and months. 808 tinyMonths = localeData.tinyMonthNames; 809 tinyWeekdays = localeData.tinyWeekdayNames; 810 811 // Standalone month names. 812 standAloneMonths = localeData.longStandAloneMonthNames; 813 shortStandAloneMonths = localeData.shortStandAloneMonthNames; 814 tinyStandAloneMonths = localeData.tinyStandAloneMonthNames; 815 816 // Standalone weekdays. 817 standAloneWeekdays = localeData.longStandAloneWeekdayNames; 818 shortStandAloneWeekdays = localeData.shortStandAloneWeekdayNames; 819 tinyStandAloneWeekdays = localeData.tinyStandAloneWeekdayNames; 820 } 821 822 /** 823 * Package private: used by SimpleDateFormat 824 * Gets the index for the given time zone ID to obtain the time zone 825 * strings for formatting. The time zone ID is just for programmatic 826 * lookup. NOT LOCALIZED!!! 827 * @param ID the given time zone ID. 828 * @return the index of the given time zone ID. Returns -1 if 829 * the given time zone ID can't be located in the DateFormatSymbols object. 830 * @see java.util.SimpleTimeZone 831 */ getZoneIndex(String ID)832 final int getZoneIndex(String ID) { 833 String[][] zoneStrings = getZoneStringsWrapper(); 834 835 /* 836 * getZoneIndex has been re-written for performance reasons. instead of 837 * traversing the zoneStrings array every time, we cache the last used zone 838 * index 839 */ 840 if (lastZoneIndex < zoneStrings.length && ID.equals(zoneStrings[lastZoneIndex][0])) { 841 return lastZoneIndex; 842 } 843 844 /* slow path, search entire list */ 845 for (int index = 0; index < zoneStrings.length; index++) { 846 if (ID.equals(zoneStrings[index][0])) { 847 lastZoneIndex = index; 848 return index; 849 } 850 } 851 852 return -1; 853 } 854 855 /** 856 * Wrapper method to the getZoneStrings(), which is called from inside 857 * the java.text package and not to mutate the returned arrays, so that 858 * it does not need to create a defensive copy. 859 */ getZoneStringsWrapper()860 final String[][] getZoneStringsWrapper() { 861 if (isSubclassObject()) { 862 return getZoneStrings(); 863 } else { 864 return getZoneStringsImpl(false); 865 } 866 } 867 internalZoneStrings()868 private final synchronized String[][] internalZoneStrings() { 869 if (zoneStrings == null) { 870 zoneStrings = TimeZoneNames.getZoneStrings(locale); 871 // If icu4c doesn't have a name, our array contains a null. TimeZone.getDisplayName 872 // knows how to format GMT offsets (and, unlike icu4c, has accurate data). http://b/8128460. 873 for (String[] zone : zoneStrings) { 874 String id = zone[0]; 875 if (zone[1] == null) { 876 zone[1] = 877 TimeZone.getTimeZone(id).getDisplayName(false, TimeZone.LONG, locale); 878 } 879 if (zone[2] == null) { 880 zone[2] = 881 TimeZone.getTimeZone(id).getDisplayName(false, TimeZone.SHORT, locale); 882 } 883 if (zone[3] == null) { 884 zone[3] = TimeZone.getTimeZone(id).getDisplayName(true, TimeZone.LONG, locale); 885 } 886 if (zone[4] == null) { 887 zone[4] = 888 TimeZone.getTimeZone(id).getDisplayName(true, TimeZone.SHORT, locale); 889 } 890 } 891 } 892 return zoneStrings; 893 } 894 getZoneStringsImpl(boolean needsCopy)895 private final String[][] getZoneStringsImpl(boolean needsCopy) { 896 String[][] zoneStrings = internalZoneStrings(); 897 898 if (!needsCopy) { 899 return zoneStrings; 900 } 901 902 int len = zoneStrings.length; 903 String[][] aCopy = new String[len][]; 904 for (int i = 0; i < len; i++) { 905 aCopy[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length); 906 } 907 return aCopy; 908 } 909 isSubclassObject()910 private boolean isSubclassObject() { 911 return !getClass().getName().equals("java.text.DateFormatSymbols"); 912 } 913 914 /** 915 * Clones all the data members from the source DateFormatSymbols to 916 * the target DateFormatSymbols. This is only for subclasses. 917 * @param src the source DateFormatSymbols. 918 * @param dst the target DateFormatSymbols. 919 */ copyMembers(DateFormatSymbols src, DateFormatSymbols dst)920 private void copyMembers(DateFormatSymbols src, DateFormatSymbols dst) 921 { 922 dst.eras = Arrays.copyOf(src.eras, src.eras.length); 923 dst.months = Arrays.copyOf(src.months, src.months.length); 924 dst.shortMonths = Arrays.copyOf(src.shortMonths, src.shortMonths.length); 925 dst.weekdays = Arrays.copyOf(src.weekdays, src.weekdays.length); 926 dst.shortWeekdays = Arrays.copyOf(src.shortWeekdays, src.shortWeekdays.length); 927 dst.ampms = Arrays.copyOf(src.ampms, src.ampms.length); 928 if (src.zoneStrings != null) { 929 dst.zoneStrings = src.getZoneStringsImpl(true); 930 } else { 931 dst.zoneStrings = null; 932 } 933 dst.localPatternChars = src.localPatternChars; 934 dst.cachedHashCode = 0; 935 936 dst.tinyMonths = src.tinyMonths; 937 dst.tinyWeekdays = src.tinyWeekdays; 938 939 dst.standAloneMonths = src.standAloneMonths; 940 dst.shortStandAloneMonths = src.shortStandAloneMonths; 941 dst.tinyStandAloneMonths = src.tinyStandAloneMonths; 942 943 dst.standAloneWeekdays = src.standAloneWeekdays; 944 dst.shortStandAloneWeekdays = src.shortStandAloneWeekdays; 945 dst.tinyStandAloneWeekdays = src.tinyStandAloneWeekdays; 946 } 947 readObject(ObjectInputStream stream)948 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 949 stream.defaultReadObject(); 950 951 if (serialVersionOnStream < 1) { 952 LocaleData localeData = LocaleData.get(locale); 953 initializeSupplementaryData(localeData); 954 } 955 956 serialVersionOnStream = currentSerialVersion; 957 } 958 959 /** 960 * Write out the default serializable data, after ensuring the 961 * <code>zoneStrings</code> field is initialized in order to make 962 * sure the backward compatibility. 963 * 964 * @since 1.6 965 */ writeObject(ObjectOutputStream stream)966 private void writeObject(ObjectOutputStream stream) throws IOException { 967 internalZoneStrings(); 968 stream.defaultWriteObject(); 969 } 970 } 971