• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2000, 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 package sun.util.calendar;
28 
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.Properties;
34 import java.util.TimeZone;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.concurrent.ConcurrentMap;
37 
38 /**
39  * <code>CalendarSystem</code> is an abstract class that defines the
40  * programming interface to deal with calendar date and time.
41  *
42  * <p><code>CalendarSystem</code> instances are singletons. For
43  * example, there exists only one Gregorian calendar instance in the
44  * Java runtime environment. A singleton instance can be obtained
45  * calling one of the static factory methods.
46  *
47  * <h4>CalendarDate</h4>
48  *
49  * <p>For the methods in a <code>CalendarSystem</code> that manipulate
50  * a <code>CalendarDate</code>, <code>CalendarDate</code>s that have
51  * been created by the <code>CalendarSystem</code> must be
52  * specified. Otherwise, the methods throw an exception. This is
53  * because, for example, a Chinese calendar date can't be understood
54  * by the Hebrew calendar system.
55  *
56  * <h4>Calendar names</h4>
57  *
58  * Each calendar system has a unique name to be identified. The Java
59  * runtime in this release supports the following calendar systems.
60  *
61  * <pre>
62  *  Name          Calendar System
63  *  ---------------------------------------
64  *  gregorian     Gregorian Calendar
65  *  julian        Julian Calendar
66  *  japanese      Japanese Imperial Calendar
67  * </pre>
68  *
69  * @see CalendarDate
70  * @author Masayoshi Okutsu
71  * @since 1.5
72  */
73 
74 public abstract class CalendarSystem {
75 
76     /////////////////////// Calendar Factory Methods /////////////////////////
77 
78     // BEGIN Android-changed: avoid reflection for loading calendar classes.
79     // // Map of calendar names and calendar class names
80     // private static ConcurrentMap<String, String> names;
81     // Map of calendar names and calendar classes;
82     private static final Map<String, Class<?>> names;
83 
84     // Map of calendar names and CalendarSystem instances
85     private static final ConcurrentMap<String, CalendarSystem> calendars =
86             new ConcurrentHashMap<>();
87 
88     static {
89         names = new HashMap<>();
90         names.put("gregorian", Gregorian.class);
91         names.put("japanese", LocalGregorianCalendar.class);
92         names.put("julian", JulianCalendar.class);
93     // END Android-changed: avoid reflection for loading calendar classes.
94         /*
95         "hebrew", "HebrewCalendar",
96         "iso8601", "ISOCalendar",
97         "taiwanese", "LocalGregorianCalendar",
98         "thaibuddhist", "LocalGregorianCalendar",
99         */
100     }
101 
102     // BEGIN Android-removed: avoid reflection for loading calendar classes.
103     /*
104     private static void initNames() {
105         ConcurrentMap<String,String> nameMap = new ConcurrentHashMap<>();
106 
107         // Associate a calendar name with its class name and the
108         // calendar class name with its date class name.
109         StringBuilder clName = new StringBuilder();
110         for (int i = 0; i < namePairs.length; i += 2) {
111             clName.setLength(0);
112             String cl = clName.append(PACKAGE_NAME).append(namePairs[i+1]).toString();
113             nameMap.put(namePairs[i], cl);
114         }
115         synchronized (CalendarSystem.class) {
116             if (!initialized) {
117                 names = nameMap;
118                 calendars = new ConcurrentHashMap<>();
119                 initialized = true;
120             }
121         }
122     }
123     */
124     // END Android-removed: avoid reflection for loading calendar classes.
125 
126     private final static Gregorian GREGORIAN_INSTANCE = new Gregorian();
127 
128     /**
129      * Returns the singleton instance of the <code>Gregorian</code>
130      * calendar system.
131      *
132      * @return the <code>Gregorian</code> instance
133      */
getGregorianCalendar()134     public static Gregorian getGregorianCalendar() {
135         return GREGORIAN_INSTANCE;
136     }
137 
138     /**
139      * Returns a <code>CalendarSystem</code> specified by the calendar
140      * name. The calendar name has to be one of the supported calendar
141      * names.
142      *
143      * @param calendarName the calendar name
144      * @return the <code>CalendarSystem</code> specified by
145      * <code>calendarName</code>, or null if there is no
146      * <code>CalendarSystem</code> associated with the given calendar name.
147      */
forName(String calendarName)148     public static CalendarSystem forName(String calendarName) {
149         if ("gregorian".equals(calendarName)) {
150             return GREGORIAN_INSTANCE;
151         }
152 
153         // Android-changed: remove lazy initialization, use classes instead of class names.
154 
155         CalendarSystem cal = calendars.get(calendarName);
156         if (cal != null) {
157             return cal;
158         }
159 
160         Class<?> calendarClass = names.get(calendarName);
161         if (calendarClass == null) {
162             return null; // Unknown calendar name
163         }
164 
165         if (calendarClass.isAssignableFrom(LocalGregorianCalendar.class)) {
166             // Create the specific kind of local Gregorian calendar system
167             cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName);
168         } else {
169             try {
170                 cal = (CalendarSystem) calendarClass.newInstance();
171             } catch (Exception e) {
172                 throw new InternalError(e);
173             }
174         }
175         if (cal == null) {
176             return null;
177         }
178         CalendarSystem cs =  calendars.putIfAbsent(calendarName, cal);
179         return (cs == null) ? cal : cs;
180     }
181 
182     /**
183      * Returns a {@link Properties} loaded from lib/calendars.properties.
184      *
185      * @return a {@link Properties} loaded from lib/calendars.properties
186      * @throws IOException if an error occurred when reading from the input stream
187      * @throws IllegalArgumentException if the input stream contains any malformed
188      *                                  Unicode escape sequences
189      */
getCalendarProperties()190     public static Properties getCalendarProperties() throws IOException {
191         // Android-changed: load calendar Properties from resources.
192         Properties calendarProps = new Properties();
193         try (InputStream is = ClassLoader.getSystemResourceAsStream("calendars.properties")) {
194             calendarProps.load(is);
195         }
196         return calendarProps;
197     }
198 
199     //////////////////////////////// Calendar API //////////////////////////////////
200 
201     /**
202      * Returns the name of this calendar system.
203      */
getName()204     public abstract String getName();
205 
getCalendarDate()206     public abstract CalendarDate getCalendarDate();
207 
208     /**
209      * Calculates calendar fields from the specified number of
210      * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC
211      * (Gregorian). This method doesn't check overflow or underflow
212      * when adjusting the millisecond value (representing UTC) with
213      * the time zone offsets (i.e., the GMT offset and amount of
214      * daylight saving).
215      *
216      * @param millis the offset value in milliseconds from January 1,
217      * 1970 00:00:00 UTC (Gregorian).
218      * @return a <code>CalendarDate</code> instance that contains the
219      * calculated calendar field values.
220      */
getCalendarDate(long millis)221     public abstract CalendarDate getCalendarDate(long millis);
222 
getCalendarDate(long millis, CalendarDate date)223     public abstract CalendarDate getCalendarDate(long millis, CalendarDate date);
224 
getCalendarDate(long millis, TimeZone zone)225     public abstract CalendarDate getCalendarDate(long millis, TimeZone zone);
226 
227     /**
228      * Constructs a <code>CalendarDate</code> that is specific to this
229      * calendar system. All calendar fields have their initial
230      * values. The {@link TimeZone#getDefault() default time zone} is
231      * set to the instance.
232      *
233      * @return a <code>CalendarDate</code> instance that contains the initial
234      * calendar field values.
235      */
newCalendarDate()236     public abstract CalendarDate newCalendarDate();
237 
newCalendarDate(TimeZone zone)238     public abstract CalendarDate newCalendarDate(TimeZone zone);
239 
240     /**
241      * Returns the number of milliseconds since the Epoch, January 1,
242      * 1970 00:00:00 UTC (Gregorian), represented by the specified
243      * <code>CalendarDate</code>.
244      *
245      * @param date the <code>CalendarDate</code> from which the time
246      * value is calculated
247      * @return the number of milliseconds since the Epoch.
248      */
getTime(CalendarDate date)249     public abstract long getTime(CalendarDate date);
250 
251     /**
252      * Returns the length in days of the specified year by
253      * <code>date</code>. This method does not perform the
254      * normalization with the specified <code>CalendarDate</code>. The
255      * <code>CalendarDate</code> must be normalized to get a correct
256      * value.
257      */
getYearLength(CalendarDate date)258     public abstract int getYearLength(CalendarDate date);
259 
260     /**
261      * Returns the number of months of the specified year. This method
262      * does not perform the normalization with the specified
263      * <code>CalendarDate</code>. The <code>CalendarDate</code> must
264      * be normalized to get a correct value.
265      */
getYearLengthInMonths(CalendarDate date)266     public abstract int getYearLengthInMonths(CalendarDate date);
267 
268     /**
269      * Returns the length in days of the month specified by the calendar
270      * date. This method does not perform the normalization with the
271      * specified calendar date. The <code>CalendarDate</code> must
272      * be normalized to get a correct value.
273      *
274      * @param date the date from which the month value is obtained
275      * @return the number of days in the month
276      * @exception IllegalArgumentException if the specified calendar date
277      * doesn't have a valid month value in this calendar system.
278      */
getMonthLength(CalendarDate date)279     public abstract int getMonthLength(CalendarDate date); // no setter
280 
281     /**
282      * Returns the length in days of a week in this calendar
283      * system. If this calendar system has multiple radix weeks, this
284      * method returns only one of them.
285      */
getWeekLength()286     public abstract int getWeekLength();
287 
288     /**
289      * Returns the <code>Era</code> designated by the era name that
290      * has to be known to this calendar system. If no Era is
291      * applicable to this calendar system, null is returned.
292      *
293      * @param eraName the name of the era
294      * @return the <code>Era</code> designated by
295      * <code>eraName</code>, or <code>null</code> if no Era is
296      * applicable to this calendar system or the specified era name is
297      * not known to this calendar system.
298      */
getEra(String eraName)299     public abstract Era getEra(String eraName);
300 
301     /**
302      * Returns valid <code>Era</code>s of this calendar system. The
303      * return value is sorted in the descendant order. (i.e., the first
304      * element of the returned array is the oldest era.) If no era is
305      * applicable to this calendar system, <code>null</code> is returned.
306      *
307      * @return an array of valid <code>Era</code>s, or
308      * <code>null</code> if no era is applicable to this calendar
309      * system.
310      */
getEras()311     public abstract Era[] getEras();
312 
313     /**
314      * @throws IllegalArgumentException if the specified era name is
315      * unknown to this calendar system.
316      * @see Era
317      */
setEra(CalendarDate date, String eraName)318     public abstract void setEra(CalendarDate date, String eraName);
319 
320     /**
321      * Returns a <code>CalendarDate</code> of the n-th day of week
322      * which is on, after or before the specified date. For example, the
323      * first Sunday in April 2002 (Gregorian) can be obtained as
324      * below:
325      *
326      * <pre><code>
327      * Gregorian cal = CalendarSystem.getGregorianCalendar();
328      * CalendarDate date = cal.newCalendarDate();
329      * date.setDate(2004, cal.APRIL, 1);
330      * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date);
331      * // firstSun represents April 4, 2004.
332      * </code></pre>
333      *
334      * This method returns a new <code>CalendarDate</code> instance
335      * and doesn't modify the original date.
336      *
337      * @param nth specifies the n-th one. A positive number specifies
338      * <em>on or after</em> the <code>date</code>. A non-positive number
339      * specifies <em>on or before</em> the <code>date</code>.
340      * @param dayOfWeek the day of week
341      * @param date the date
342      * @return the date of the nth <code>dayOfWeek</code> after
343      * or before the specified <code>CalendarDate</code>
344      */
getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date)345     public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek,
346                                                  CalendarDate date);
347 
setTimeOfDay(CalendarDate date, int timeOfDay)348     public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay);
349 
350     /**
351      * Checks whether the calendar fields specified by <code>date</code>
352      * represents a valid date and time in this calendar system. If the
353      * given date is valid, <code>date</code> is marked as <em>normalized</em>.
354      *
355      * @param date the <code>CalendarDate</code> to be validated
356      * @return <code>true</code> if all the calendar fields are consistent,
357      * otherwise, <code>false</code> is returned.
358      * @exception NullPointerException if the specified
359      * <code>date</code> is <code>null</code>
360      */
validate(CalendarDate date)361     public abstract boolean validate(CalendarDate date);
362 
363     /**
364      * Normalizes calendar fields in the specified
365      * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED
366      * undefined} fields are set to correct values. The actual
367      * normalization process is calendar system dependent.
368      *
369      * @param date the calendar date to be validated
370      * @return <code>true</code> if all fields have been normalized;
371      * <code>false</code> otherwise.
372      * @exception NullPointerException if the specified
373      * <code>date</code> is <code>null</code>
374      */
normalize(CalendarDate date)375     public abstract boolean normalize(CalendarDate date);
376 }
377