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