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     // Map of calendar names and calendar classes;
79     private static final Map<String, Class<?>> names;
80 
81     // Android-changed: Don't use reflection for Class.forName every time.
82 
83     static {
84         names = new HashMap<>();
85         names.put("gregorian", Gregorian.class);
86         names.put("japanese", LocalGregorianCalendar.class);
87         names.put("julian", JulianCalendar.class);
88         // names.put("hebrew", "HebrewCalendar");
89         // names.put("iso8601", "ISOCalendar");
90         // names.put("taiwanese", "LocalGregorianCalendar");
91         // names.put("thaibuddhist", "LocalGregorianCalendar");
92     }
93 
94     // Map of calendar names and CalendarSystem instances
95     private static final ConcurrentMap<String, CalendarSystem> calendars =
96             new ConcurrentHashMap<>();
97 
98     private final static Gregorian GREGORIAN_INSTANCE = new Gregorian();
99 
100     /**
101      * Returns the singleton instance of the <code>Gregorian</code>
102      * calendar system.
103      *
104      * @return the <code>Gregorian</code> instance
105      */
getGregorianCalendar()106     public static Gregorian getGregorianCalendar() {
107         return GREGORIAN_INSTANCE;
108     }
109 
110     /**
111      * Returns a <code>CalendarSystem</code> specified by the calendar
112      * name. The calendar name has to be one of the supported calendar
113      * names.
114      *
115      * @param calendarName the calendar name
116      * @return the <code>CalendarSystem</code> specified by
117      * <code>calendarName</code>, or null if there is no
118      * <code>CalendarSystem</code> associated with the given calendar name.
119      */
forName(String calendarName)120     public static CalendarSystem forName(String calendarName) {
121         if ("gregorian".equals(calendarName)) {
122             return GREGORIAN_INSTANCE;
123         }
124 
125         //Android-changed: remove lazy initialization, use classes instead of class names.
126 
127         CalendarSystem cal = calendars.get(calendarName);
128         if (cal != null) {
129             return cal;
130         }
131 
132         Class<?> calendarClass = names.get(calendarName);
133         if (calendarClass == null) {
134             return null; // Unknown calendar name
135         }
136 
137         if (calendarClass.isAssignableFrom(LocalGregorianCalendar.class)) {
138             // Create the specific kind of local Gregorian calendar system
139             cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName);
140         } else {
141             try {
142                 cal = (CalendarSystem) calendarClass.newInstance();
143             } catch (Exception e) {
144                 throw new RuntimeException("internal error", e);
145             }
146         }
147         if (cal == null) {
148             return null;
149         }
150 
151         CalendarSystem cs =  calendars.putIfAbsent(calendarName, cal);
152         return (cs == null) ? cal : cs;
153     }
154 
155     /**
156      * Returns a {@link Properties} loaded from lib/calendars.properties.
157      *
158      * @return a {@link Properties} loaded from lib/calendars.properties
159      * @throws IOException if an error occurred when reading from the input stream
160      * @throws IllegalArgumentException if the input stream contains any malformed
161      *                                  Unicode escape sequences
162      */
getCalendarProperties()163     public static Properties getCalendarProperties() throws IOException {
164         // Android-changed: load from resources.
165         Properties calendarProps = new Properties();
166         try (InputStream is = ClassLoader.getSystemResourceAsStream("calendars.properties")) {
167             calendarProps.load(is);
168         }
169         return calendarProps;
170     }
171 
172     //////////////////////////////// Calendar API //////////////////////////////////
173 
174     /**
175      * Returns the name of this calendar system.
176      */
getName()177     public abstract String getName();
178 
getCalendarDate()179     public abstract CalendarDate getCalendarDate();
180 
181     /**
182      * Calculates calendar fields from the specified number of
183      * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC
184      * (Gregorian). This method doesn't check overflow or underflow
185      * when adjusting the millisecond value (representing UTC) with
186      * the time zone offsets (i.e., the GMT offset and amount of
187      * daylight saving).
188      *
189      * @param millis the offset value in milliseconds from January 1,
190      * 1970 00:00:00 UTC (Gregorian).
191      * @return a <code>CalendarDate</code> instance that contains the
192      * calculated calendar field values.
193      */
getCalendarDate(long millis)194     public abstract CalendarDate getCalendarDate(long millis);
195 
getCalendarDate(long millis, CalendarDate date)196     public abstract CalendarDate getCalendarDate(long millis, CalendarDate date);
197 
getCalendarDate(long millis, TimeZone zone)198     public abstract CalendarDate getCalendarDate(long millis, TimeZone zone);
199 
200     /**
201      * Constructs a <code>CalendarDate</code> that is specific to this
202      * calendar system. All calendar fields have their initial
203      * values. The {@link TimeZone#getDefault() default time zone} is
204      * set to the instance.
205      *
206      * @return a <code>CalendarDate</code> instance that contains the initial
207      * calendar field values.
208      */
newCalendarDate()209     public abstract CalendarDate newCalendarDate();
210 
newCalendarDate(TimeZone zone)211     public abstract CalendarDate newCalendarDate(TimeZone zone);
212 
213     /**
214      * Returns the number of milliseconds since the Epoch, January 1,
215      * 1970 00:00:00 UTC (Gregorian), represented by the specified
216      * <code>CalendarDate</code>.
217      *
218      * @param date the <code>CalendarDate</code> from which the time
219      * value is calculated
220      * @return the number of milliseconds since the Epoch.
221      */
getTime(CalendarDate date)222     public abstract long getTime(CalendarDate date);
223 
224     /**
225      * Returns the length in days of the specified year by
226      * <code>date</code>. This method does not perform the
227      * normalization with the specified <code>CalendarDate</code>. The
228      * <code>CalendarDate</code> must be normalized to get a correct
229      * value.
230      */
getYearLength(CalendarDate date)231     public abstract int getYearLength(CalendarDate date);
232 
233     /**
234      * Returns the number of months of the specified year. This method
235      * does not perform the normalization with the specified
236      * <code>CalendarDate</code>. The <code>CalendarDate</code> must
237      * be normalized to get a correct value.
238      */
getYearLengthInMonths(CalendarDate date)239     public abstract int getYearLengthInMonths(CalendarDate date);
240 
241     /**
242      * Returns the length in days of the month specified by the calendar
243      * date. This method does not perform the normalization with the
244      * specified calendar date. The <code>CalendarDate</code> must
245      * be normalized to get a correct value.
246      *
247      * @param date the date from which the month value is obtained
248      * @return the number of days in the month
249      * @exception IllegalArgumentException if the specified calendar date
250      * doesn't have a valid month value in this calendar system.
251      */
getMonthLength(CalendarDate date)252     public abstract int getMonthLength(CalendarDate date); // no setter
253 
254     /**
255      * Returns the length in days of a week in this calendar
256      * system. If this calendar system has multiple radix weeks, this
257      * method returns only one of them.
258      */
getWeekLength()259     public abstract int getWeekLength();
260 
261     /**
262      * Returns the <code>Era</code> designated by the era name that
263      * has to be known to this calendar system. If no Era is
264      * applicable to this calendar system, null is returned.
265      *
266      * @param eraName the name of the era
267      * @return the <code>Era</code> designated by
268      * <code>eraName</code>, or <code>null</code> if no Era is
269      * applicable to this calendar system or the specified era name is
270      * not known to this calendar system.
271      */
getEra(String eraName)272     public abstract Era getEra(String eraName);
273 
274     /**
275      * Returns valid <code>Era</code>s of this calendar system. The
276      * return value is sorted in the descendant order. (i.e., the first
277      * element of the returned array is the oldest era.) If no era is
278      * applicable to this calendar system, <code>null</code> is returned.
279      *
280      * @return an array of valid <code>Era</code>s, or
281      * <code>null</code> if no era is applicable to this calendar
282      * system.
283      */
getEras()284     public abstract Era[] getEras();
285 
286     /**
287      * @throws IllegalArgumentException if the specified era name is
288      * unknown to this calendar system.
289      * @see Era
290      */
setEra(CalendarDate date, String eraName)291     public abstract void setEra(CalendarDate date, String eraName);
292 
293     /**
294      * Returns a <code>CalendarDate</code> of the n-th day of week
295      * which is on, after or before the specified date. For example, the
296      * first Sunday in April 2002 (Gregorian) can be obtained as
297      * below:
298      *
299      * <pre><code>
300      * Gregorian cal = CalendarSystem.getGregorianCalendar();
301      * CalendarDate date = cal.newCalendarDate();
302      * date.setDate(2004, cal.APRIL, 1);
303      * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date);
304      * // firstSun represents April 4, 2004.
305      * </code></pre>
306      *
307      * This method returns a new <code>CalendarDate</code> instance
308      * and doesn't modify the original date.
309      *
310      * @param nth specifies the n-th one. A positive number specifies
311      * <em>on or after</em> the <code>date</code>. A non-positive number
312      * specifies <em>on or before</em> the <code>date</code>.
313      * @param dayOfWeek the day of week
314      * @param date the date
315      * @return the date of the nth <code>dayOfWeek</code> after
316      * or before the specified <code>CalendarDate</code>
317      */
getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date)318     public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek,
319                                                  CalendarDate date);
320 
setTimeOfDay(CalendarDate date, int timeOfDay)321     public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay);
322 
323     /**
324      * Checks whether the calendar fields specified by <code>date</code>
325      * represents a valid date and time in this calendar system. If the
326      * given date is valid, <code>date</code> is marked as <em>normalized</em>.
327      *
328      * @param date the <code>CalendarDate</code> to be validated
329      * @return <code>true</code> if all the calendar fields are consistent,
330      * otherwise, <code>false</code> is returned.
331      * @exception NullPointerException if the specified
332      * <code>date</code> is <code>null</code>
333      */
validate(CalendarDate date)334     public abstract boolean validate(CalendarDate date);
335 
336     /**
337      * Normalizes calendar fields in the specified
338      * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED
339      * undefined} fields are set to correct values. The actual
340      * normalization process is calendar system dependent.
341      *
342      * @param date the calendar date to be validated
343      * @return <code>true</code> if all fields have been normalized;
344      * <code>false</code> otherwise.
345      * @exception NullPointerException if the specified
346      * <code>date</code> is <code>null</code>
347      */
normalize(CalendarDate date)348     public abstract boolean normalize(CalendarDate date);
349 }
350