DateFormatSymbols.java revision 8ee76d4d3ee58109f859964e56d5c7f3d8c566d9
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// BEGIN android-note
19// The icu implementation used was changed from icu4j to icu4jni.
20// END android-note
21
22package java.text;
23
24import java.io.IOException;
25import java.io.ObjectOutputStream;
26import java.io.Serializable;
27import java.util.Arrays;
28import java.util.Locale;
29// BEGIN android-added
30import java.util.ResourceBundle;
31// END android-added
32
33// BEGIN android-added
34import com.ibm.icu4jni.util.LocaleData;
35import com.ibm.icu4jni.util.Resources;
36// END android-added
37/**
38 * Encapsulates localizable date-time formatting data, such as the names of the
39 * months, the names of the days of the week, and the time zone data.
40 * {@code DateFormat} and {@code SimpleDateFormat} both use
41 * {@code DateFormatSymbols} to encapsulate this information.
42 * <p>
43 * Typically you shouldn't use {@code DateFormatSymbols} directly. Rather, you
44 * are encouraged to create a date/time formatter with the {@code DateFormat}
45 * class's factory methods: {@code getTimeInstance}, {@code getDateInstance},
46 * or {@code getDateTimeInstance}. These methods automatically create a
47 * {@code DateFormatSymbols} for the formatter so that you don't have to. After
48 * the formatter is created, you may modify its format pattern using the
49 * {@code setPattern} method. For more information about creating formatters
50 * using {@code DateFormat}'s factory methods, see {@link DateFormat}.
51 * <p>
52 * If you decide to create a date/time formatter with a specific format pattern
53 * for a specific locale, you can do so with:
54 * <blockquote>
55 *
56 * <pre>
57 * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
58 * </pre>
59 *
60 * </blockquote>
61 * <p>
62 * {@code DateFormatSymbols} objects can be cloned. When you obtain a
63 * {@code DateFormatSymbols} object, feel free to modify the date/time
64 * formatting data. For instance, you can replace the localized date/time format
65 * pattern characters with the ones that you feel easy to remember or you can
66 * change the representative cities to your favorite ones.
67 * <p>
68 * New {@code DateFormatSymbols} subclasses may be added to support
69 * {@code SimpleDateFormat} for date/time formatting for additional locales.
70 *
71 * @see DateFormat
72 * @see SimpleDateFormat
73 */
74public class DateFormatSymbols implements Serializable, Cloneable {
75
76    private static final long serialVersionUID = -5987973545549424702L;
77
78    private String localPatternChars;
79
80    String[] ampms, eras, months, shortMonths, shortWeekdays, weekdays;
81
82    // Localized display names.
83    String[][] zoneStrings;
84    // Has the user called setZoneStrings?
85    transient boolean customZoneStrings;
86
87    // BEGIN android-removed
88    // transient private com.ibm.icu4jni.text.DateFormatSymbols icuSymbols;
89    // END android-removed
90
91// BEGIN android-added
92    /**
93     * Locale, necessary to lazily load time zone strings. We force the time
94     * zone names to load upon serialization, so this will never be needed
95     * post deserialization.
96     */
97    transient final Locale locale;
98
99    /**
100     * Gets zone strings, initializing them if necessary. Does not create
101     * a defensive copy, so make sure you do so before exposing the returned
102     * arrays to clients.
103     */
104    synchronized String[][] internalZoneStrings() {
105        if (zoneStrings == null) {
106            zoneStrings = Resources.getDisplayTimeZones(locale.toString());
107        }
108        return zoneStrings;
109    }
110// END android-added
111
112    /**
113     * Constructs a new {@code DateFormatSymbols} instance containing the
114     * symbols for the default locale.
115     */
116    public DateFormatSymbols() {
117        this(Locale.getDefault());
118    }
119
120    /**
121     * Constructs a new {@code DateFormatSymbols} instance containing the
122     * symbols for the specified locale.
123     *
124     * @param locale
125     *            the locale.
126     */
127    public DateFormatSymbols(Locale locale) {
128        this.locale = locale;
129        // BEGIN android-changed
130        this.localPatternChars = SimpleDateFormat.patternChars;
131        LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale);
132        this.ampms = localeData.amPm;
133        this.eras = localeData.eras;
134        this.months = localeData.longMonthNames;
135        this.shortMonths = localeData.shortMonthNames;
136        this.weekdays = localeData.longWeekdayNames;
137        this.shortWeekdays = localeData.shortWeekdayNames;
138        // END android-changed
139    }
140
141
142    private void writeObject(ObjectOutputStream oos) throws IOException {
143        // BEGIN android-changed
144        internalZoneStrings();
145        // END android-changed
146        oos.defaultWriteObject();
147    }
148
149    // BEGIN android-removed
150    // DateFormatSymbols(Locale locale,
151    //         com.ibm.icu4jni.text.DateFormatSymbols icuSymbols) {
152    //
153    //     this.icuSymbols = icuSymbols;
154    //     localPatternChars = icuSymbols.getLocalPatternChars();
155    //     ampms = icuSymbols.getAmPmStrings();
156    //     eras = icuSymbols.getEras();
157    //     months = icuSymbols.getMonths();
158    //     shortMonths = icuSymbols.getShortMonths();
159    //     shortWeekdays = icuSymbols.getShortWeekdays();
160    //     weekdays = icuSymbols.getWeekdays();
161    // }
162    // END android-removed
163
164    @Override
165    public Object clone() {
166        // BEGIN android-changed
167        try {
168            return super.clone();
169        } catch (CloneNotSupportedException e) {
170            throw new AssertionError();
171        }
172        // END android-changed
173    }
174
175    /**
176     * Compares this object with the specified object and indicates if they are
177     * equal.
178     *
179     * @param object
180     *            the object to compare with this object.
181     * @return {@code true} if {@code object} is an instance of
182     *         {@code DateFormatSymbols} and has the same symbols as this
183     *         object, {@code false} otherwise.
184     * @see #hashCode
185     */
186    @Override
187    public boolean equals(Object object) {
188        if (this == object) {
189            return true;
190        }
191        if (!(object instanceof DateFormatSymbols)) {
192            return false;
193        }
194
195        // BEGIN android-removed
196        // if (zoneStrings == null) {
197        //     zoneStrings = icuSymbols.getZoneStrings();
198        // }
199        // END android-removed
200        DateFormatSymbols obj = (DateFormatSymbols) object;
201        // BEGIN android-removed
202        // if (obj.zoneStrings == null) {
203        //     obj.zoneStrings = obj.icuSymbols.getZoneStrings();
204        // }
205        // END android-removed
206        if (!localPatternChars.equals(obj.localPatternChars)) {
207            return false;
208        }
209        if (!Arrays.equals(ampms, obj.ampms)) {
210            return false;
211        }
212        if (!Arrays.equals(eras, obj.eras)) {
213            return false;
214        }
215        if (!Arrays.equals(months, obj.months)) {
216            return false;
217        }
218        if (!Arrays.equals(shortMonths, obj.shortMonths)) {
219            return false;
220        }
221        if (!Arrays.equals(shortWeekdays, obj.shortWeekdays)) {
222            return false;
223        }
224        if (!Arrays.equals(weekdays, obj.weekdays)) {
225            return false;
226        }
227        // BEGIN android-changed
228        // Quick check that may keep us from having to load the zone strings.
229        if (zoneStrings == null && obj.zoneStrings == null
230                    && !locale.equals(obj.locale)) {
231            return false;
232        }
233        // Make sure zone strings are loaded.
234        internalZoneStrings();
235        obj.internalZoneStrings();
236        // END android-changed
237        if (zoneStrings.length != obj.zoneStrings.length) {
238            return false;
239        }
240        for (String[] element : zoneStrings) {
241            if (element.length != element.length) {
242                return false;
243            }
244            for (int j = 0; j < element.length; j++) {
245                if (element[j] != element[j]
246                        && !(element[j].equals(element[j]))) {
247                    return false;
248                }
249            }
250        }
251        return true;
252    }
253
254    /**
255     * Returns the array of strings which represent AM and PM. Use the
256     * {@link java.util.Calendar} constants {@code Calendar.AM} and
257     * {@code Calendar.PM} as indices for the array.
258     *
259     * @return an array of strings.
260     */
261    public String[] getAmPmStrings() {
262        return ampms.clone();
263    }
264
265    /**
266     * Returns the array of strings which represent BC and AD. Use the
267     * {@link java.util.Calendar} constants {@code GregorianCalendar.BC} and
268     * {@code GregorianCalendar.AD} as indices for the array.
269     *
270     * @return an array of strings.
271     */
272    public String[] getEras() {
273        return eras.clone();
274    }
275
276    /**
277     * Returns the pattern characters used by {@link SimpleDateFormat} to
278     * specify date and time fields.
279     *
280     * @return a string containing the pattern characters.
281     */
282    public String getLocalPatternChars() {
283        return localPatternChars;
284    }
285
286    /**
287     * Returns the array of strings containing the full names of the months. Use
288     * the {@link java.util.Calendar} constants {@code Calendar.JANUARY} etc. as
289     * indices for the array.
290     *
291     * @return an array of strings.
292     */
293    public String[] getMonths() {
294        return months.clone();
295    }
296
297    /**
298     * Returns the array of strings containing the abbreviated names of the
299     * months. Use the {@link java.util.Calendar} constants
300     * {@code Calendar.JANUARY} etc. as indices for the array.
301     *
302     * @return an array of strings.
303     */
304    public String[] getShortMonths() {
305        return shortMonths.clone();
306    }
307
308    /**
309     * Returns the array of strings containing the abbreviated names of the days
310     * of the week. Use the {@link java.util.Calendar} constants
311     * {@code Calendar.SUNDAY} etc. as indices for the array.
312     *
313     * @return an array of strings.
314     */
315    public String[] getShortWeekdays() {
316        return shortWeekdays.clone();
317    }
318
319    /**
320     * Returns the array of strings containing the full names of the days of the
321     * week. Use the {@link java.util.Calendar} constants
322     * {@code Calendar.SUNDAY} etc. as indices for the array.
323     *
324     * @return an array of strings.
325     */
326    public String[] getWeekdays() {
327        return weekdays.clone();
328    }
329
330    /**
331     * Returns the two-dimensional array of strings containing the names of the
332     * time zones. Each element in the array is an array of five strings, the
333     * first is a TimeZone ID, the second and third are the full and abbreviated
334     * time zone names for standard time, and the fourth and fifth are the full
335     * and abbreviated names for daylight time.
336     *
337     * @return a two-dimensional array of strings.
338     */
339    public String[][] getZoneStrings() {
340        // BEGIN android-changed
341        return Resources.clone2dStringArray(internalZoneStrings());
342        // END android-changed
343    }
344
345    @Override
346    public int hashCode() {
347        // BEGIN android-changed
348        String[][] zoneStrings = internalZoneStrings();
349        // END android-changed
350        int hashCode;
351        hashCode = localPatternChars.hashCode();
352        for (String element : ampms) {
353            hashCode += element.hashCode();
354        }
355        for (String element : eras) {
356            hashCode += element.hashCode();
357        }
358        for (String element : months) {
359            hashCode += element.hashCode();
360        }
361        for (String element : shortMonths) {
362            hashCode += element.hashCode();
363        }
364        for (String element : shortWeekdays) {
365            hashCode += element.hashCode();
366        }
367        for (String element : weekdays) {
368            hashCode += element.hashCode();
369        }
370        for (String[] element : zoneStrings) {
371            for (int j = 0; j < element.length; j++) {
372                if (element[j] != null) {
373                    hashCode += element[j].hashCode();
374                }
375            }
376        }
377        return hashCode;
378    }
379
380    /**
381     * Sets the array of strings which represent AM and PM. Use the
382     * {@link java.util.Calendar} constants {@code Calendar.AM} and
383     * {@code Calendar.PM} as indices for the array.
384     *
385     * @param data
386     *            the array of strings for AM and PM.
387     */
388    public void setAmPmStrings(String[] data) {
389        ampms = data.clone();
390    }
391
392    /**
393     * Sets the array of Strings which represent BC and AD. Use the
394     * {@link java.util.Calendar} constants {@code GregorianCalendar.BC} and
395     * {@code GregorianCalendar.AD} as indices for the array.
396     *
397     * @param data
398     *            the array of strings for BC and AD.
399     */
400    public void setEras(String[] data) {
401        eras = data.clone();
402    }
403
404    /**
405     * Sets the pattern characters used by {@link SimpleDateFormat} to specify
406     * date and time fields.
407     *
408     * @param data
409     *            the string containing the pattern characters.
410     * @throws NullPointerException
411     *            if {@code data} is null
412     */
413    public void setLocalPatternChars(String data) {
414        if (data == null) {
415            throw new NullPointerException();
416        }
417        localPatternChars = data;
418    }
419
420    /**
421     * Sets the array of strings containing the full names of the months. Use
422     * the {@link java.util.Calendar} constants {@code Calendar.JANUARY} etc. as
423     * indices for the array.
424     *
425     * @param data
426     *            the array of strings.
427     */
428    public void setMonths(String[] data) {
429        months = data.clone();
430    }
431
432    /**
433     * Sets the array of strings containing the abbreviated names of the months.
434     * Use the {@link java.util.Calendar} constants {@code Calendar.JANUARY}
435     * etc. as indices for the array.
436     *
437     * @param data
438     *            the array of strings.
439     */
440    public void setShortMonths(String[] data) {
441        shortMonths = data.clone();
442    }
443
444    /**
445     * Sets the array of strings containing the abbreviated names of the days of
446     * the week. Use the {@link java.util.Calendar} constants
447     * {@code Calendar.SUNDAY} etc. as indices for the array.
448     *
449     * @param data
450     *            the array of strings.
451     */
452    public void setShortWeekdays(String[] data) {
453        shortWeekdays = data.clone();
454    }
455
456    /**
457     * Sets the array of strings containing the full names of the days of the
458     * week. Use the {@link java.util.Calendar} constants
459     * {@code Calendar.SUNDAY} etc. as indices for the array.
460     *
461     * @param data
462     *            the array of strings.
463     */
464    public void setWeekdays(String[] data) {
465        weekdays = data.clone();
466    }
467
468    /**
469     * Sets the two-dimensional array of strings containing the names of the
470     * time zones. Each element in the array is an array of five strings, the
471     * first is a TimeZone ID, and second and third are the full and abbreviated
472     * time zone names for standard time, and the fourth and fifth are the full
473     * and abbreviated names for daylight time.
474     *
475     * @param data
476     *            the two-dimensional array of strings.
477     */
478    public void setZoneStrings(String[][] data) {
479        zoneStrings = Resources.clone2dStringArray(data);
480        customZoneStrings = true;
481    }
482}
483