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