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