• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package java.text;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.Serializable;
24 import java.util.Arrays;
25 import java.util.Locale;
26 import libcore.icu.ICU;
27 import libcore.icu.LocaleData;
28 import libcore.icu.TimeZones;
29 
30 /**
31  * Encapsulates localized date-time formatting data, such as the names of the
32  * months, the names of the days of the week, and the time zone data.
33  * {@code DateFormat} and {@code SimpleDateFormat} both use
34  * {@code DateFormatSymbols} to encapsulate this information.
35  *
36  * <p>Typically you shouldn't use {@code DateFormatSymbols} directly. Rather, you
37  * are encouraged to create a date/time formatter with the {@code DateFormat}
38  * class's factory methods: {@code getTimeInstance}, {@code getDateInstance},
39  * or {@code getDateTimeInstance}. These methods automatically create a
40  * {@code DateFormatSymbols} for the formatter so that you don't have to. After
41  * the formatter is created, you may modify its format pattern using the
42  * {@code setPattern} method. For more information about creating formatters
43  * using {@code DateFormat}'s factory methods, see {@link DateFormat}.
44  *
45  * <p>Direct use of {@code DateFormatSymbols} is likely to be less efficient
46  * because the implementation cannot make assumptions about user-supplied/user-modifiable data
47  * to the same extent that it can with its own built-in data.
48  *
49  * @see DateFormat
50  * @see SimpleDateFormat
51  */
52 public class DateFormatSymbols implements Serializable, Cloneable {
53 
54     private static final long serialVersionUID = -5987973545549424702L;
55 
56     private String localPatternChars;
57 
58     String[] ampms, eras, months, shortMonths, shortWeekdays, weekdays;
59 
60     // These are used to implement ICU/Android extensions.
61     transient String[] longStandAloneMonths;
62     transient String[] shortStandAloneMonths;
63     transient String[] longStandAloneWeekdays;
64     transient String[] shortStandAloneWeekdays;
65 
66     // Localized display names.
67     String[][] zoneStrings;
68     // Has the user called setZoneStrings?
69     transient boolean customZoneStrings;
70 
71     /**
72      * Locale, necessary to lazily load time zone strings. We force the time
73      * zone names to load upon serialization, so this will never be needed
74      * post deserialization.
75      */
76     transient final Locale locale;
77 
78     /**
79      * Gets zone strings, initializing them if necessary. Does not create
80      * a defensive copy, so make sure you do so before exposing the returned
81      * arrays to clients.
82      */
internalZoneStrings()83     synchronized String[][] internalZoneStrings() {
84         if (zoneStrings == null) {
85             zoneStrings = TimeZones.getZoneStrings(locale);
86         }
87         return zoneStrings;
88     }
89 
90     /**
91      * Constructs a new {@code DateFormatSymbols} instance containing the
92      * symbols for the user's default locale.
93      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
94      */
DateFormatSymbols()95     public DateFormatSymbols() {
96         this(Locale.getDefault());
97     }
98 
99     /**
100      * Constructs a new {@code DateFormatSymbols} instance containing the
101      * symbols for the specified locale.
102      *
103      * @param locale
104      *            the locale.
105      */
DateFormatSymbols(Locale locale)106     public DateFormatSymbols(Locale locale) {
107         this.locale = locale;
108         this.localPatternChars = SimpleDateFormat.PATTERN_CHARS;
109         LocaleData localeData = LocaleData.get(locale);
110         this.ampms = localeData.amPm;
111         this.eras = localeData.eras;
112         this.months = localeData.longMonthNames;
113         this.shortMonths = localeData.shortMonthNames;
114         this.weekdays = localeData.longWeekdayNames;
115         this.shortWeekdays = localeData.shortWeekdayNames;
116 
117         // ICU/Android extensions.
118         this.longStandAloneMonths = localeData.longStandAloneMonthNames;
119         this.shortStandAloneMonths = localeData.shortStandAloneMonthNames;
120         this.longStandAloneWeekdays = localeData.longStandAloneWeekdayNames;
121         this.shortStandAloneWeekdays = localeData.shortStandAloneWeekdayNames;
122     }
123 
124     /**
125      * Returns a new {@code DateFormatSymbols} instance for the user's default locale.
126      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
127      *
128      * @return an instance of {@code DateFormatSymbols}
129      * @since 1.6
130      */
getInstance()131     public static final DateFormatSymbols getInstance() {
132         return getInstance(Locale.getDefault());
133     }
134 
135     /**
136      * Returns a new {@code DateFormatSymbols} for the given locale.
137      *
138      * @param locale the locale
139      * @return an instance of {@code DateFormatSymbols}
140      * @throws NullPointerException if {@code locale == null}
141      * @since 1.6
142      */
getInstance(Locale locale)143     public static final DateFormatSymbols getInstance(Locale locale) {
144         if (locale == null) {
145             throw new NullPointerException();
146         }
147         return new DateFormatSymbols(locale);
148     }
149 
150     /**
151      * Returns an array of locales for which custom {@code DateFormatSymbols} instances
152      * are available.
153      * <p>Note that Android does not support user-supplied locale service providers.
154      * @since 1.6
155      */
getAvailableLocales()156     public static Locale[] getAvailableLocales() {
157         return ICU.getAvailableDateFormatSymbolsLocales();
158     }
159 
readObject(ObjectInputStream ois)160     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
161         ois.defaultReadObject();
162 
163         // The RI doesn't have these fields, so we'll have to fall back and do the best we can.
164         longStandAloneMonths = months;
165         shortStandAloneMonths = shortMonths;
166         longStandAloneWeekdays = weekdays;
167         shortStandAloneWeekdays = shortWeekdays;
168     }
169 
writeObject(ObjectOutputStream oos)170     private void writeObject(ObjectOutputStream oos) throws IOException {
171         internalZoneStrings();
172         oos.defaultWriteObject();
173     }
174 
175     @Override
clone()176     public Object clone() {
177         try {
178             return super.clone();
179         } catch (CloneNotSupportedException e) {
180             throw new AssertionError();
181         }
182     }
183 
184     /**
185      * Compares this object with the specified object and indicates if they are
186      * equal.
187      *
188      * @param object
189      *            the object to compare with this object.
190      * @return {@code true} if {@code object} is an instance of
191      *         {@code DateFormatSymbols} and has the same symbols as this
192      *         object, {@code false} otherwise.
193      * @see #hashCode
194      */
195     @Override
equals(Object object)196     public boolean equals(Object object) {
197         if (this == object) {
198             return true;
199         }
200         if (!(object instanceof DateFormatSymbols)) {
201             return false;
202         }
203         DateFormatSymbols rhs = (DateFormatSymbols) object;
204         return localPatternChars.equals(rhs.localPatternChars) &&
205                 Arrays.equals(ampms, rhs.ampms) &&
206                 Arrays.equals(eras, rhs.eras) &&
207                 Arrays.equals(months, rhs.months) &&
208                 Arrays.equals(shortMonths, rhs.shortMonths) &&
209                 Arrays.equals(shortWeekdays, rhs.shortWeekdays) &&
210                 Arrays.equals(weekdays, rhs.weekdays) &&
211                 timeZoneStringsEqual(this, rhs);
212     }
213 
timeZoneStringsEqual(DateFormatSymbols lhs, DateFormatSymbols rhs)214     private static boolean timeZoneStringsEqual(DateFormatSymbols lhs, DateFormatSymbols rhs) {
215         // Quick check that may keep us from having to load the zone strings.
216         // Note that different locales may have the same strings, so the opposite check isn't valid.
217         if (lhs.zoneStrings == null && rhs.zoneStrings == null && lhs.locale.equals(rhs.locale)) {
218             return true;
219         }
220         // Make sure zone strings are loaded, then check.
221         return Arrays.deepEquals(lhs.internalZoneStrings(), rhs.internalZoneStrings());
222     }
223 
224     @Override
toString()225     public String toString() {
226         // 'locale' isn't part of the externally-visible state.
227         // 'zoneStrings' is so large, we just print a representative value.
228         return getClass().getName() +
229                 "[amPmStrings=" + Arrays.toString(ampms) +
230                 ",customZoneStrings=" + customZoneStrings +
231                 ",eras=" + Arrays.toString(eras) +
232                 ",localPatternChars=" + localPatternChars +
233                 ",months=" + Arrays.toString(months) +
234                 ",shortMonths=" + Arrays.toString(shortMonths) +
235                 ",shortWeekdays=" + Arrays.toString(shortWeekdays) +
236                 ",weekdays=" + Arrays.toString(weekdays) +
237                 ",zoneStrings=[" + Arrays.toString(internalZoneStrings()[0]) + "...]" +
238                 "]";
239     }
240 
241     /**
242      * Returns the array of strings which represent AM and PM. Use the
243      * {@link java.util.Calendar} constants {@code Calendar.AM} and
244      * {@code Calendar.PM} as indices for the array.
245      *
246      * @return an array of strings.
247      */
getAmPmStrings()248     public String[] getAmPmStrings() {
249         return ampms.clone();
250     }
251 
252     /**
253      * Returns the array of strings which represent BC and AD. Use the
254      * {@link java.util.Calendar} constants {@code GregorianCalendar.BC} and
255      * {@code GregorianCalendar.AD} as indices for the array.
256      *
257      * @return an array of strings.
258      */
getEras()259     public String[] getEras() {
260         return eras.clone();
261     }
262 
263     /**
264      * Returns the pattern characters used by {@link SimpleDateFormat} to
265      * specify date and time fields.
266      *
267      * @return a string containing the pattern characters.
268      */
getLocalPatternChars()269     public String getLocalPatternChars() {
270         return localPatternChars;
271     }
272 
273     /**
274      * Returns the array of strings containing the full names of the months. Use
275      * the {@link java.util.Calendar} constants {@code Calendar.JANUARY} etc. as
276      * indices for the array.
277      *
278      * @return an array of strings.
279      */
getMonths()280     public String[] getMonths() {
281         return months.clone();
282     }
283 
284     /**
285      * Returns the array of strings containing the abbreviated names of the
286      * months. Use the {@link java.util.Calendar} constants
287      * {@code Calendar.JANUARY} etc. as indices for the array.
288      *
289      * @return an array of strings.
290      */
getShortMonths()291     public String[] getShortMonths() {
292         return shortMonths.clone();
293     }
294 
295     /**
296      * Returns the array of strings containing the abbreviated names of the days
297      * of the week. Use the {@link java.util.Calendar} constants
298      * {@code Calendar.SUNDAY} etc. as indices for the array.
299      *
300      * @return an array of strings.
301      */
getShortWeekdays()302     public String[] getShortWeekdays() {
303         return shortWeekdays.clone();
304     }
305 
306     /**
307      * Returns the array of strings containing the full names of the days of the
308      * week. Use the {@link java.util.Calendar} constants
309      * {@code Calendar.SUNDAY} etc. as indices for the array.
310      *
311      * @return an array of strings.
312      */
getWeekdays()313     public String[] getWeekdays() {
314         return weekdays.clone();
315     }
316 
317     /**
318      * Returns the two-dimensional array of strings containing localized names for time zones.
319      * Each row is an array of five strings:
320      * <ul>
321      * <li>The time zone ID, for example "America/Los_Angeles".
322      *     This is not localized, and is used as a key into the table.
323      * <li>The long display name, for example "Pacific Standard Time".
324      * <li>The short display name, for example "PST".
325      * <li>The long display name for DST, for example "Pacific Daylight Time".
326      *     This is the non-DST long name for zones that have never had DST, for
327      *     example "Central Standard Time" for "Canada/Saskatchewan".
328      * <li>The short display name for DST, for example "PDT". This is the
329      *     non-DST short name for zones that have never had DST, for example
330      *     "CST" for "Canada/Saskatchewan".
331      * </ul>
332      */
getZoneStrings()333     public String[][] getZoneStrings() {
334         return clone2dStringArray(internalZoneStrings());
335     }
336 
clone2dStringArray(String[][] array)337     private static String[][] clone2dStringArray(String[][] array) {
338         String[][] result = new String[array.length][];
339         for (int i = 0; i < array.length; ++i) {
340             result[i] = array[i].clone();
341         }
342         return result;
343     }
344 
345     @Override
hashCode()346     public int hashCode() {
347         String[][] zoneStrings = internalZoneStrings();
348         int hashCode;
349         hashCode = localPatternChars.hashCode();
350         for (String element : ampms) {
351             hashCode += element.hashCode();
352         }
353         for (String element : eras) {
354             hashCode += element.hashCode();
355         }
356         for (String element : months) {
357             hashCode += element.hashCode();
358         }
359         for (String element : shortMonths) {
360             hashCode += element.hashCode();
361         }
362         for (String element : shortWeekdays) {
363             hashCode += element.hashCode();
364         }
365         for (String element : weekdays) {
366             hashCode += element.hashCode();
367         }
368         for (String[] element : zoneStrings) {
369             for (int j = 0; j < element.length; j++) {
370                 if (element[j] != null) {
371                     hashCode += element[j].hashCode();
372                 }
373             }
374         }
375         return hashCode;
376     }
377 
378     /**
379      * Sets the array of strings which represent AM and PM. Use the
380      * {@link java.util.Calendar} constants {@code Calendar.AM} and
381      * {@code Calendar.PM} as indices for the array.
382      *
383      * @param data
384      *            the array of strings for AM and PM.
385      */
setAmPmStrings(String[] data)386     public void setAmPmStrings(String[] data) {
387         ampms = data.clone();
388     }
389 
390     /**
391      * Sets the array of Strings which represent BC and AD. Use the
392      * {@link java.util.Calendar} constants {@code GregorianCalendar.BC} and
393      * {@code GregorianCalendar.AD} as indices for the array.
394      *
395      * @param data
396      *            the array of strings for BC and AD.
397      */
setEras(String[] data)398     public void setEras(String[] data) {
399         eras = data.clone();
400     }
401 
402     /**
403      * Sets the pattern characters used by {@link SimpleDateFormat} to specify
404      * date and time fields.
405      *
406      * @param data
407      *            the string containing the pattern characters.
408      * @throws NullPointerException
409      *            if {@code data} is null
410      */
setLocalPatternChars(String data)411     public void setLocalPatternChars(String data) {
412         if (data == null) {
413             throw new NullPointerException();
414         }
415         localPatternChars = data;
416     }
417 
418     /**
419      * Sets the array of strings containing the full names of the months. Use
420      * the {@link java.util.Calendar} constants {@code Calendar.JANUARY} etc. as
421      * indices for the array.
422      *
423      * @param data
424      *            the array of strings.
425      */
setMonths(String[] data)426     public void setMonths(String[] data) {
427         months = data.clone();
428     }
429 
430     /**
431      * Sets the array of strings containing the abbreviated names of the months.
432      * Use the {@link java.util.Calendar} constants {@code Calendar.JANUARY}
433      * etc. as indices for the array.
434      *
435      * @param data
436      *            the array of strings.
437      */
setShortMonths(String[] data)438     public void setShortMonths(String[] data) {
439         shortMonths = data.clone();
440     }
441 
442     /**
443      * Sets the array of strings containing the abbreviated names of the days of
444      * the week. Use the {@link java.util.Calendar} constants
445      * {@code Calendar.SUNDAY} etc. as indices for the array.
446      *
447      * @param data
448      *            the array of strings.
449      */
setShortWeekdays(String[] data)450     public void setShortWeekdays(String[] data) {
451         shortWeekdays = data.clone();
452     }
453 
454     /**
455      * Sets the array of strings containing the full names of the days of the
456      * week. Use the {@link java.util.Calendar} constants
457      * {@code Calendar.SUNDAY} etc. as indices for the array.
458      *
459      * @param data
460      *            the array of strings.
461      */
setWeekdays(String[] data)462     public void setWeekdays(String[] data) {
463         weekdays = data.clone();
464     }
465 
466     /**
467      * Sets the two-dimensional array of strings containing localized names for time zones.
468      * See {@link #getZoneStrings} for details.
469      * @throws IllegalArgumentException if any row has fewer than 5 elements.
470      * @throws NullPointerException if {@code zoneStrings == null}.
471      */
setZoneStrings(String[][] zoneStrings)472     public void setZoneStrings(String[][] zoneStrings) {
473         if (zoneStrings == null) {
474             throw new NullPointerException();
475         }
476         for (String[] row : zoneStrings) {
477             if (row.length < 5) {
478                 throw new IllegalArgumentException(Arrays.toString(row) + ".length < 5");
479             }
480         }
481         this.zoneStrings = clone2dStringArray(zoneStrings);
482         this.customZoneStrings = true;
483     }
484 }
485