Home | History | Annotate | Download | only in calendar
      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      */
    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      */
    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      */
    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      */
    204     public abstract String getName();
    205 
    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      */
    221     public abstract CalendarDate getCalendarDate(long millis);
    222 
    223     public abstract CalendarDate getCalendarDate(long millis, CalendarDate date);
    224 
    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      */
    236     public abstract CalendarDate newCalendarDate();
    237 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    345     public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek,
    346                                                  CalendarDate date);
    347 
    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      */
    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      */
    375     public abstract boolean normalize(CalendarDate date);
    376 }
    377