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                 Class<?> cl = Class.forName(className);
 161                 cal = (CalendarSystem) cl.newInstance();
 162             } catch (Exception e) {
 163                 throw new InternalError(e);
 164             }
 165         }
 166         if (cal == null) {
 167             return null;
 168         }
 169         CalendarSystem cs =  calendars.putIfAbsent(calendarName, cal);
 170         return (cs == null) ? cal : cs;
 171     }
 172 
 173     //////////////////////////////// Calendar API //////////////////////////////////
 174 
 175     /**
 176      * Returns the name of this calendar system.
 177      */
 178     public abstract String getName();
 179 
 180     public abstract CalendarDate getCalendarDate();
 181 
 182     /**
 183      * Calculates calendar fields from the specified number of
 184      * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC
 185      * (Gregorian). This method doesn't check overflow or underflow
 186      * when adjusting the millisecond value (representing UTC) with
 187      * the time zone offsets (i.e., the GMT offset and amount of
 188      * daylight saving).
 189      *
 190      * @param millis the offset value in milliseconds from January 1,
 191      * 1970 00:00:00 UTC (Gregorian).
 192      * @return a <code>CalendarDate</code> instance that contains the
 193      * calculated calendar field values.
 194      */
 195     public abstract CalendarDate getCalendarDate(long millis);
 196 
 197     public abstract CalendarDate getCalendarDate(long millis, CalendarDate date);
 198 
 199     public abstract CalendarDate getCalendarDate(long millis, TimeZone zone);
 200 
 201     /**
 202      * Constructs a <code>CalendarDate</code> that is specific to this
 203      * calendar system. All calendar fields have their initial
 204      * values. The {@link TimeZone#getDefault() default time zone} is
 205      * set to the instance.
 206      *
 207      * @return a <code>CalendarDate</code> instance that contains the initial
 208      * calendar field values.
 209      */
 210     public abstract CalendarDate newCalendarDate();
 211 
 212     public abstract CalendarDate newCalendarDate(TimeZone zone);
 213 
 214     /**
 215      * Returns the number of milliseconds since the Epoch, January 1,
 216      * 1970 00:00:00 UTC (Gregorian), represented by the specified
 217      * <code>CalendarDate</code>.
 218      *
 219      * @param date the <code>CalendarDate</code> from which the time
 220      * value is calculated
 221      * @return the number of milliseconds since the Epoch.
 222      */
 223     public abstract long getTime(CalendarDate date);
 224 
 225     /**
 226      * Returns the length in days of the specified year by
 227      * <code>date</code>. This method does not perform the
 228      * normalization with the specified <code>CalendarDate</code>. The
 229      * <code>CalendarDate</code> must be normalized to get a correct
 230      * value.
 231      */
 232     public abstract int getYearLength(CalendarDate date);
 233 
 234     /**
 235      * Returns the number of months of the specified year. This method
 236      * does not perform the normalization with the specified
 237      * <code>CalendarDate</code>. The <code>CalendarDate</code> must
 238      * be normalized to get a correct value.
 239      */
 240     public abstract int getYearLengthInMonths(CalendarDate date);
 241 
 242     /**
 243      * Returns the length in days of the month specified by the calendar
 244      * date. This method does not perform the normalization with the
 245      * specified calendar date. The <code>CalendarDate</code> must
 246      * be normalized to get a correct value.
 247      *
 248      * @param date the date from which the month value is obtained
 249      * @return the number of days in the month
 250      * @exception IllegalArgumentException if the specified calendar date
 251      * doesn't have a valid month value in this calendar system.
 252      */
 253     public abstract int getMonthLength(CalendarDate date); // no setter
 254 
 255     /**
 256      * Returns the length in days of a week in this calendar
 257      * system. If this calendar system has multiple radix weeks, this
 258      * method returns only one of them.
 259      */
 260     public abstract int getWeekLength();
 261 
 262     /**
 263      * Returns the <code>Era</code> designated by the era name that
 264      * has to be known to this calendar system. If no Era is
 265      * applicable to this calendar system, null is returned.
 266      *
 267      * @param eraName the name of the era
 268      * @return the <code>Era</code> designated by
 269      * <code>eraName</code>, or <code>null</code> if no Era is
 270      * applicable to this calendar system or the specified era name is
 271      * not known to this calendar system.
 272      */
 273     public abstract Era getEra(String eraName);
 274 
 275     /**
 276      * Returns valid <code>Era</code>s of this calendar system. The
 277      * return value is sorted in the descendant order. (i.e., the first
 278      * element of the returned array is the oldest era.) If no era is
 279      * applicable to this calendar system, <code>null</code> is returned.
 280      *
 281      * @return an array of valid <code>Era</code>s, or
 282      * <code>null</code> if no era is applicable to this calendar
 283      * system.
 284      */
 285     public abstract Era[] getEras();
 286 
 287     /**
 288      * @throws IllegalArgumentException if the specified era name is
 289      * unknown to this calendar system.
 290      * @see Era
 291      */
 292     public abstract void setEra(CalendarDate date, String eraName);
 293 
 294     /**
 295      * Returns a <code>CalendarDate</code> of the n-th day of week
 296      * which is on, after or before the specified date. For example, the
 297      * first Sunday in April 2002 (Gregorian) can be obtained as
 298      * below:
 299      *
 300      * <pre><code>
 301      * Gregorian cal = CalendarSystem.getGregorianCalendar();
 302      * CalendarDate date = cal.newCalendarDate();
 303      * date.setDate(2004, cal.APRIL, 1);
 304      * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date);
 305      * // firstSun represents April 4, 2004.
 306      * </code></pre>
 307      *
 308      * This method returns a new <code>CalendarDate</code> instance
 309      * and doesn't modify the original date.
 310      *
 311      * @param nth specifies the n-th one. A positive number specifies
 312      * <em>on or after</em> the <code>date</code>. A non-positive number
 313      * specifies <em>on or before</em> the <code>date</code>.
 314      * @param dayOfWeek the day of week
 315      * @param date the date
 316      * @return the date of the nth <code>dayOfWeek</code> after
 317      * or before the specified <code>CalendarDate</code>
 318      */
 319     public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek,
 320                                                  CalendarDate date);
 321 
 322     public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay);
 323 
 324     /**
 325      * Checks whether the calendar fields specified by <code>date</code>
 326      * represents a valid date and time in this calendar system. If the
 327      * given date is valid, <code>date</code> is marked as <em>normalized</em>.
 328      *
 329      * @param date the <code>CalendarDate</code> to be validated
 330      * @return <code>true</code> if all the calendar fields are consistent,
 331      * otherwise, <code>false</code> is returned.
 332      * @exception NullPointerException if the specified
 333      * <code>date</code> is <code>null</code>
 334      */
 335     public abstract boolean validate(CalendarDate date);
 336 
 337     /**
 338      * Normalizes calendar fields in the specified
 339      * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED
 340      * undefined} fields are set to correct values. The actual
 341      * normalization process is calendar system dependent.
 342      *
 343      * @param date the calendar date to be validated
 344      * @return <code>true</code> if all fields have been normalized;
 345      * <code>false</code> otherwise.
 346      * @exception NullPointerException if the specified
 347      * <code>date</code> is <code>null</code>
 348      */
 349     public abstract boolean normalize(CalendarDate date);
 350 }