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 */ getGregorianCalendar()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 */ forName(String calendarName)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 */ getCalendarProperties()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 */ getName()204 public abstract String getName(); 205 getCalendarDate()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 */ getCalendarDate(long millis)221 public abstract CalendarDate getCalendarDate(long millis); 222 getCalendarDate(long millis, CalendarDate date)223 public abstract CalendarDate getCalendarDate(long millis, CalendarDate date); 224 getCalendarDate(long millis, TimeZone zone)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 */ newCalendarDate()236 public abstract CalendarDate newCalendarDate(); 237 newCalendarDate(TimeZone zone)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 */ getTime(CalendarDate date)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 */ getYearLength(CalendarDate date)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 */ getYearLengthInMonths(CalendarDate date)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 */ getMonthLength(CalendarDate date)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 */ getWeekLength()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 */ getEra(String eraName)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 */ getEras()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 */ setEra(CalendarDate date, String eraName)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 */ getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date)345 public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, 346 CalendarDate date); 347 setTimeOfDay(CalendarDate date, int timeOfDay)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 */ validate(CalendarDate date)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 */ normalize(CalendarDate date)375 public abstract boolean normalize(CalendarDate date); 376 } 377