1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util.calendar;
  27 
  28 import java.util.TimeZone;
  29 import java.util.concurrent.ConcurrentHashMap;
  30 import java.util.concurrent.ConcurrentMap;
  31 
  32 /**
  33  * <code>CalendarSystem</code> is an abstract class that defines the
  34  * programming interface to deal with calendar date and time.
  35  *
  36  * <p><code>CalendarSystem</code> instances are singletons. For
  37  * example, there exists only one Gregorian calendar instance in the
  38  * Java runtime environment. A singleton instance can be obtained
  39  * calling one of the static factory methods.
  40  *
  41  * <h4>CalendarDate</h4>
  42  *
  43  * <p>For the methods in a <code>CalendarSystem</code> that manipulate
  44  * a <code>CalendarDate</code>, <code>CalendarDate</code>s that have
  45  * been created by the <code>CalendarSystem</code> must be
  46  * specified. Otherwise, the methods throw an exception. This is
  47  * because, for example, a Chinese calendar date can't be understood
  48  * by the Hebrew calendar system.
  49  *
  50  * <h4>Calendar names</h4>
  51  *
  52  * Each calendar system has a unique name to be identified. The Java
  53  * runtime in this release supports the following calendar systems.
  54  *
  55  * <pre>
  56  *  Name          Calendar System
  57  *  ---------------------------------------
  58  *  gregorian     Gregorian Calendar
  59  *  julian        Julian Calendar
  60  *  japanese      Japanese Imperial Calendar
  61  * </pre>
  62  *
  63  * @see CalendarDate
  64  * @author Masayoshi Okutsu
  65  * @since 1.5
  66  */
  67 
  68 public abstract class CalendarSystem {
  69 
  70     /////////////////////// Calendar Factory Methods /////////////////////////
  71 
  72     private static volatile boolean initialized;
  73 
  74     // Map of calendar names and calendar class names
  75     private static ConcurrentMap<String, String> names;
  76 
  77     // Map of calendar names and CalendarSystem instances
  78     private static ConcurrentMap<String,CalendarSystem> calendars;
  79 
  80     private static final String PACKAGE_NAME = "sun.util.calendar.";
  81 
  82     private static final String[] namePairs = {
  83         "gregorian", "Gregorian",
  84         "japanese", "LocalGregorianCalendar",
  85         "julian", "JulianCalendar",
  86         /*
  87         "hebrew", "HebrewCalendar",
  88         "iso8601", "ISOCalendar",
  89         "taiwanese", "LocalGregorianCalendar",
  90         "thaibuddhist", "LocalGregorianCalendar",
  91         */
  92     };
  93 
  94     private static void initNames() {
  95         ConcurrentMap<String,String> nameMap = new ConcurrentHashMap<>();
  96 
  97         // Associate a calendar name with its class name and the
  98         // calendar class name with its date class name.
  99         StringBuilder clName = new StringBuilder();
 100         for (int i = 0; i < namePairs.length; i += 2) {
 101             clName.setLength(0);
 102             String cl = clName.append(PACKAGE_NAME).append(namePairs[i+1]).toString();
 103             nameMap.put(namePairs[i], cl);
 104         }
 105         synchronized (CalendarSystem.class) {
 106             if (!initialized) {
 107                 names = nameMap;
 108                 calendars = new ConcurrentHashMap<>();
 109                 initialized = true;
 110             }
 111         }
 112     }
 113 
 114     private static final Gregorian GREGORIAN_INSTANCE = new Gregorian();
 115 
 116     /**
 117      * Returns the singleton instance of the <code>Gregorian</code>
 118      * calendar system.
 119      *
 120      * @return the <code>Gregorian</code> instance
 121      */
 122     public static Gregorian getGregorianCalendar() {
 123         return GREGORIAN_INSTANCE;
 124     }
 125 
 126     /**
 127      * Returns a <code>CalendarSystem</code> specified by the calendar
 128      * name. The calendar name has to be one of the supported calendar
 129      * names.
 130      *
 131      * @param calendarName the calendar name
 132      * @return the <code>CalendarSystem</code> specified by
 133      * <code>calendarName</code>, or null if there is no
 134      * <code>CalendarSystem</code> associated with the given calendar name.
 135      */
 136     public static CalendarSystem forName(String calendarName) {
 137         if ("gregorian".equals(calendarName)) {
 138             return GREGORIAN_INSTANCE;
 139         }
 140 
 141         if (!initialized) {
 142             initNames();
 143         }
 144 
 145         CalendarSystem cal = calendars.get(calendarName);
 146         if (cal != null) {
 147             return cal;
 148         }
 149 
 150         String className = names.get(calendarName);
 151         if (className == null) {
 152             return null; // Unknown calendar name
 153         }
 154 
 155         if (className.endsWith("LocalGregorianCalendar")) {
 156             // Create the specific kind of local Gregorian calendar system
 157             cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName);
 158         } else {
 159             try {
 160                 @SuppressWarnings("deprecation")
 161                 Object tmp = Class.forName(className).newInstance();
 162                 cal = (CalendarSystem) tmp;
 163             } catch (Exception e) {
 164                 throw new InternalError(e);
 165             }
 166         }
 167         if (cal == null) {
 168             return null;
 169         }
 170         CalendarSystem cs =  calendars.putIfAbsent(calendarName, cal);
 171         return (cs == null) ? cal : cs;
 172     }
 173 
 174     //////////////////////////////// Calendar API //////////////////////////////////
 175 
 176     /**
 177      * Returns the name of this calendar system.
 178      */
 179     public abstract String getName();
 180 
 181     public abstract CalendarDate getCalendarDate();
 182 
 183     /**
 184      * Calculates calendar fields from the specified number of
 185      * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC
 186      * (Gregorian). This method doesn't check overflow or underflow
 187      * when adjusting the millisecond value (representing UTC) with
 188      * the time zone offsets (i.e., the GMT offset and amount of
 189      * daylight saving).
 190      *
 191      * @param millis the offset value in milliseconds from January 1,
 192      * 1970 00:00:00 UTC (Gregorian).
 193      * @return a <code>CalendarDate</code> instance that contains the
 194      * calculated calendar field values.
 195      */
 196     public abstract CalendarDate getCalendarDate(long millis);
 197 
 198     public abstract CalendarDate getCalendarDate(long millis, CalendarDate date);
 199 
 200     public abstract CalendarDate getCalendarDate(long millis, TimeZone zone);
 201 
 202     /**
 203      * Constructs a <code>CalendarDate</code> that is specific to this
 204      * calendar system. All calendar fields have their initial
 205      * values. The {@link TimeZone#getDefault() default time zone} is
 206      * set to the instance.
 207      *
 208      * @return a <code>CalendarDate</code> instance that contains the initial
 209      * calendar field values.
 210      */
 211     public abstract CalendarDate newCalendarDate();
 212 
 213     public abstract CalendarDate newCalendarDate(TimeZone zone);
 214 
 215     /**
 216      * Returns the number of milliseconds since the Epoch, January 1,
 217      * 1970 00:00:00 UTC (Gregorian), represented by the specified
 218      * <code>CalendarDate</code>.
 219      *
 220      * @param date the <code>CalendarDate</code> from which the time
 221      * value is calculated
 222      * @return the number of milliseconds since the Epoch.
 223      */
 224     public abstract long getTime(CalendarDate date);
 225 
 226     /**
 227      * Returns the length in days of the specified year by
 228      * <code>date</code>. This method does not perform the
 229      * normalization with the specified <code>CalendarDate</code>. The
 230      * <code>CalendarDate</code> must be normalized to get a correct
 231      * value.
 232      */
 233     public abstract int getYearLength(CalendarDate date);
 234 
 235     /**
 236      * Returns the number of months of the specified year. This method
 237      * does not perform the normalization with the specified
 238      * <code>CalendarDate</code>. The <code>CalendarDate</code> must
 239      * be normalized to get a correct value.
 240      */
 241     public abstract int getYearLengthInMonths(CalendarDate date);
 242 
 243     /**
 244      * Returns the length in days of the month specified by the calendar
 245      * date. This method does not perform the normalization with the
 246      * specified calendar date. The <code>CalendarDate</code> must
 247      * be normalized to get a correct value.
 248      *
 249      * @param date the date from which the month value is obtained
 250      * @return the number of days in the month
 251      * @exception IllegalArgumentException if the specified calendar date
 252      * doesn't have a valid month value in this calendar system.
 253      */
 254     public abstract int getMonthLength(CalendarDate date); // no setter
 255 
 256     /**
 257      * Returns the length in days of a week in this calendar
 258      * system. If this calendar system has multiple radix weeks, this
 259      * method returns only one of them.
 260      */
 261     public abstract int getWeekLength();
 262 
 263     /**
 264      * Returns the <code>Era</code> designated by the era name that
 265      * has to be known to this calendar system. If no Era is
 266      * applicable to this calendar system, null is returned.
 267      *
 268      * @param eraName the name of the era
 269      * @return the <code>Era</code> designated by
 270      * <code>eraName</code>, or <code>null</code> if no Era is
 271      * applicable to this calendar system or the specified era name is
 272      * not known to this calendar system.
 273      */
 274     public abstract Era getEra(String eraName);
 275 
 276     /**
 277      * Returns valid <code>Era</code>s of this calendar system. The
 278      * return value is sorted in the descendant order. (i.e., the first
 279      * element of the returned array is the oldest era.) If no era is
 280      * applicable to this calendar system, <code>null</code> is returned.
 281      *
 282      * @return an array of valid <code>Era</code>s, or
 283      * <code>null</code> if no era is applicable to this calendar
 284      * system.
 285      */
 286     public abstract Era[] getEras();
 287 
 288     /**
 289      * @throws IllegalArgumentException if the specified era name is
 290      * unknown to this calendar system.
 291      * @see Era
 292      */
 293     public abstract void setEra(CalendarDate date, String eraName);
 294 
 295     /**
 296      * Returns a <code>CalendarDate</code> of the n-th day of week
 297      * which is on, after or before the specified date. For example, the
 298      * first Sunday in April 2002 (Gregorian) can be obtained as
 299      * below:
 300      *
 301      * <pre><code>
 302      * Gregorian cal = CalendarSystem.getGregorianCalendar();
 303      * CalendarDate date = cal.newCalendarDate();
 304      * date.setDate(2004, cal.APRIL, 1);
 305      * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date);
 306      * // firstSun represents April 4, 2004.
 307      * </code></pre>
 308      *
 309      * This method returns a new <code>CalendarDate</code> instance
 310      * and doesn't modify the original date.
 311      *
 312      * @param nth specifies the n-th one. A positive number specifies
 313      * <em>on or after</em> the <code>date</code>. A non-positive number
 314      * specifies <em>on or before</em> the <code>date</code>.
 315      * @param dayOfWeek the day of week
 316      * @param date the date
 317      * @return the date of the nth <code>dayOfWeek</code> after
 318      * or before the specified <code>CalendarDate</code>
 319      */
 320     public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek,
 321                                                  CalendarDate date);
 322 
 323     public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay);
 324 
 325     /**
 326      * Checks whether the calendar fields specified by <code>date</code>
 327      * represents a valid date and time in this calendar system. If the
 328      * given date is valid, <code>date</code> is marked as <em>normalized</em>.
 329      *
 330      * @param date the <code>CalendarDate</code> to be validated
 331      * @return <code>true</code> if all the calendar fields are consistent,
 332      * otherwise, <code>false</code> is returned.
 333      * @exception NullPointerException if the specified
 334      * <code>date</code> is <code>null</code>
 335      */
 336     public abstract boolean validate(CalendarDate date);
 337 
 338     /**
 339      * Normalizes calendar fields in the specified
 340      * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED
 341      * undefined} fields are set to correct values. The actual
 342      * normalization process is calendar system dependent.
 343      *
 344      * @param date the calendar date to be validated
 345      * @return <code>true</code> if all fields have been normalized;
 346      * <code>false</code> otherwise.
 347      * @exception NullPointerException if the specified
 348      * <code>date</code> is <code>null</code>
 349      */
 350     public abstract boolean normalize(CalendarDate date);
 351 }