1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2000, 2021, 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 and lazy initialization for loading calendar classes. 79 /* 80 private static volatile boolean initialized; 81 82 // Map of calendar names and calendar class names 83 private static ConcurrentMap<String, String> names; 84 */ 85 // Map of calendar names and calendar classes; 86 private static final Map<String, Class<?>> names; 87 88 // Map of calendar names and CalendarSystem instances 89 private static final ConcurrentMap<String, CalendarSystem> calendars = 90 new ConcurrentHashMap<>(); 91 // END Android-changed: avoid reflection and lazy initialization for loading calendar classes. 92 93 // BEGIN Android-changed: avoid reflection and lazy initialization for loading calendar classes. 94 /* 95 private static final String PACKAGE_NAME = "sun.util.calendar."; 96 97 private static final String[] namePairs = { 98 "gregorian", "Gregorian", 99 "japanese", "LocalGregorianCalendar", 100 "julian", "JulianCalendar", 101 */ 102 static { 103 names = new HashMap<>(); 104 names.put("gregorian", Gregorian.class); 105 names.put("japanese", LocalGregorianCalendar.class); 106 names.put("julian", JulianCalendar.class); 107 // END Android-changed: avoid reflection and lazy initialization for loading calendar classes. 108 /* 109 "hebrew", "HebrewCalendar", 110 "iso8601", "ISOCalendar", 111 "taiwanese", "LocalGregorianCalendar", 112 "thaibuddhist", "LocalGregorianCalendar", 113 */ 114 } 115 116 // BEGIN Android-removed: avoid reflection for loading calendar classes. 117 /* 118 private static void initNames() { 119 ConcurrentMap<String,String> nameMap = new ConcurrentHashMap<>(); 120 121 // Associate a calendar name with its class name and the 122 // calendar class name with its date class name. 123 StringBuilder clName = new StringBuilder(); 124 for (int i = 0; i < namePairs.length; i += 2) { 125 clName.setLength(0); 126 String cl = clName.append(PACKAGE_NAME).append(namePairs[i+1]).toString(); 127 nameMap.put(namePairs[i], cl); 128 } 129 synchronized (CalendarSystem.class) { 130 if (!initialized) { 131 names = nameMap; 132 calendars = new ConcurrentHashMap<>(); 133 initialized = true; 134 } 135 } 136 } 137 */ 138 // END Android-removed: avoid reflection for loading calendar classes. 139 140 private static final class GregorianHolder { 141 private static final Gregorian GREGORIAN_INSTANCE = new Gregorian(); 142 } 143 144 /** 145 * Returns the singleton instance of the <code>Gregorian</code> 146 * calendar system. 147 * 148 * @return the <code>Gregorian</code> instance 149 */ getGregorianCalendar()150 public static Gregorian getGregorianCalendar() { 151 return GregorianHolder.GREGORIAN_INSTANCE; 152 } 153 154 /** 155 * Returns a <code>CalendarSystem</code> specified by the calendar 156 * name. The calendar name has to be one of the supported calendar 157 * names. 158 * 159 * @param calendarName the calendar name 160 * @return the <code>CalendarSystem</code> specified by 161 * <code>calendarName</code>, or null if there is no 162 * <code>CalendarSystem</code> associated with the given calendar name. 163 */ forName(String calendarName)164 public static CalendarSystem forName(String calendarName) { 165 if ("gregorian".equals(calendarName)) { 166 return GregorianHolder.GREGORIAN_INSTANCE; 167 } 168 169 // BEGIN Android-removed: lazy initialization, use classes instead of class names. 170 /* 171 if (!initialized) { 172 initNames(); 173 } 174 */ 175 // END Android-removed: lazy initialization, use classes instead of class names. 176 177 CalendarSystem cal = calendars.get(calendarName); 178 if (cal != null) { 179 return cal; 180 } 181 182 // BEGIN Android-changed: avoid reflection for loading calendar classes. 183 /* 184 String className = names.get(calendarName); 185 if (className == null) { 186 */ 187 Class<?> calendarClass = names.get(calendarName); 188 if (calendarClass == null) { 189 // END Android-changed: avoid reflection for loading calendar classes. 190 return null; // Unknown calendar name 191 } 192 193 // BEGIN Android-changed: avoid reflection for loading calendar classes. 194 /* 195 if (className.endsWith("LocalGregorianCalendar")) { 196 */ 197 if (calendarClass.isAssignableFrom(LocalGregorianCalendar.class)) { 198 // END Android-changed: avoid reflection for loading calendar classes. 199 // Create the specific kind of local Gregorian calendar system 200 cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName); 201 } else { 202 try { 203 // BEGIN Android-changed: avoid reflection for loading calendar classes. 204 /* 205 @SuppressWarnings("deprecation") 206 Object tmp = Class.forName(className).newInstance(); 207 cal = (CalendarSystem) tmp; 208 */ 209 cal = (CalendarSystem) calendarClass.newInstance(); 210 // END Android-changed: avoid reflection for loading calendar classes. 211 } catch (Exception e) { 212 throw new InternalError(e); 213 } 214 } 215 if (cal == null) { 216 return null; 217 } 218 CalendarSystem cs = calendars.putIfAbsent(calendarName, cal); 219 return (cs == null) ? cal : cs; 220 } 221 222 // BEGIN Android-added: load calendar Properties from resources. 223 /** 224 * Returns a {@link Properties} loaded from lib/calendars.properties. 225 * 226 * @return a {@link Properties} loaded from lib/calendars.properties 227 * @throws IOException if an error occurred when reading from the input stream 228 * @throws IllegalArgumentException if the input stream contains any malformed 229 * Unicode escape sequences 230 */ getCalendarProperties()231 public static Properties getCalendarProperties() throws IOException { 232 Properties calendarProps = new Properties(); 233 try (InputStream is = ClassLoader.getSystemResourceAsStream("calendars.properties")) { 234 calendarProps.load(is); 235 } 236 return calendarProps; 237 } 238 // END Android-added: load calendar Properties from resources. 239 240 //////////////////////////////// Calendar API ////////////////////////////////// 241 242 /** 243 * Returns the name of this calendar system. 244 */ getName()245 public abstract String getName(); 246 getCalendarDate()247 public abstract CalendarDate getCalendarDate(); 248 249 /** 250 * Calculates calendar fields from the specified number of 251 * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC 252 * (Gregorian). This method doesn't check overflow or underflow 253 * when adjusting the millisecond value (representing UTC) with 254 * the time zone offsets (i.e., the GMT offset and amount of 255 * daylight saving). 256 * 257 * @param millis the offset value in milliseconds from January 1, 258 * 1970 00:00:00 UTC (Gregorian). 259 * @return a <code>CalendarDate</code> instance that contains the 260 * calculated calendar field values. 261 */ getCalendarDate(long millis)262 public abstract CalendarDate getCalendarDate(long millis); 263 getCalendarDate(long millis, CalendarDate date)264 public abstract CalendarDate getCalendarDate(long millis, CalendarDate date); 265 getCalendarDate(long millis, TimeZone zone)266 public abstract CalendarDate getCalendarDate(long millis, TimeZone zone); 267 268 /** 269 * Constructs a <code>CalendarDate</code> that is specific to this 270 * calendar system. All calendar fields have their initial 271 * values. The {@link TimeZone#getDefault() default time zone} is 272 * set to the instance. 273 * 274 * @return a <code>CalendarDate</code> instance that contains the initial 275 * calendar field values. 276 */ newCalendarDate()277 public abstract CalendarDate newCalendarDate(); 278 newCalendarDate(TimeZone zone)279 public abstract CalendarDate newCalendarDate(TimeZone zone); 280 281 /** 282 * Returns the number of milliseconds since the Epoch, January 1, 283 * 1970 00:00:00 UTC (Gregorian), represented by the specified 284 * <code>CalendarDate</code>. 285 * 286 * @param date the <code>CalendarDate</code> from which the time 287 * value is calculated 288 * @return the number of milliseconds since the Epoch. 289 */ getTime(CalendarDate date)290 public abstract long getTime(CalendarDate date); 291 292 /** 293 * Returns the length in days of the specified year by 294 * <code>date</code>. This method does not perform the 295 * normalization with the specified <code>CalendarDate</code>. The 296 * <code>CalendarDate</code> must be normalized to get a correct 297 * value. 298 */ getYearLength(CalendarDate date)299 public abstract int getYearLength(CalendarDate date); 300 301 /** 302 * Returns the number of months of the specified year. This method 303 * does not perform the normalization with the specified 304 * <code>CalendarDate</code>. The <code>CalendarDate</code> must 305 * be normalized to get a correct value. 306 */ getYearLengthInMonths(CalendarDate date)307 public abstract int getYearLengthInMonths(CalendarDate date); 308 309 /** 310 * Returns the length in days of the month specified by the calendar 311 * date. This method does not perform the normalization with the 312 * specified calendar date. The <code>CalendarDate</code> must 313 * be normalized to get a correct value. 314 * 315 * @param date the date from which the month value is obtained 316 * @return the number of days in the month 317 * @exception IllegalArgumentException if the specified calendar date 318 * doesn't have a valid month value in this calendar system. 319 */ getMonthLength(CalendarDate date)320 public abstract int getMonthLength(CalendarDate date); // no setter 321 322 /** 323 * Returns the length in days of a week in this calendar 324 * system. If this calendar system has multiple radix weeks, this 325 * method returns only one of them. 326 */ getWeekLength()327 public abstract int getWeekLength(); 328 329 /** 330 * Returns the <code>Era</code> designated by the era name that 331 * has to be known to this calendar system. If no Era is 332 * applicable to this calendar system, null is returned. 333 * 334 * @param eraName the name of the era 335 * @return the <code>Era</code> designated by 336 * <code>eraName</code>, or <code>null</code> if no Era is 337 * applicable to this calendar system or the specified era name is 338 * not known to this calendar system. 339 */ getEra(String eraName)340 public abstract Era getEra(String eraName); 341 342 /** 343 * Returns valid <code>Era</code>s of this calendar system. The 344 * return value is sorted in the descendant order. (i.e., the first 345 * element of the returned array is the oldest era.) If no era is 346 * applicable to this calendar system, <code>null</code> is returned. 347 * 348 * @return an array of valid <code>Era</code>s, or 349 * <code>null</code> if no era is applicable to this calendar 350 * system. 351 */ getEras()352 public abstract Era[] getEras(); 353 354 /** 355 * @throws IllegalArgumentException if the specified era name is 356 * unknown to this calendar system. 357 * @see Era 358 */ setEra(CalendarDate date, String eraName)359 public abstract void setEra(CalendarDate date, String eraName); 360 361 /** 362 * Returns a <code>CalendarDate</code> of the n-th day of week 363 * which is on, after or before the specified date. For example, the 364 * first Sunday in April 2002 (Gregorian) can be obtained as 365 * below: 366 * 367 * <pre><code> 368 * Gregorian cal = CalendarSystem.getGregorianCalendar(); 369 * CalendarDate date = cal.newCalendarDate(); 370 * date.setDate(2004, cal.APRIL, 1); 371 * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date); 372 * // firstSun represents April 4, 2004. 373 * </code></pre> 374 * 375 * This method returns a new <code>CalendarDate</code> instance 376 * and doesn't modify the original date. 377 * 378 * @param nth specifies the n-th one. A positive number specifies 379 * <em>on or after</em> the <code>date</code>. A non-positive number 380 * specifies <em>on or before</em> the <code>date</code>. 381 * @param dayOfWeek the day of week 382 * @param date the date 383 * @return the date of the nth <code>dayOfWeek</code> after 384 * or before the specified <code>CalendarDate</code> 385 */ getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date)386 public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, 387 CalendarDate date); 388 setTimeOfDay(CalendarDate date, int timeOfDay)389 public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay); 390 391 /** 392 * Checks whether the calendar fields specified by <code>date</code> 393 * represents a valid date and time in this calendar system. If the 394 * given date is valid, <code>date</code> is marked as <em>normalized</em>. 395 * 396 * @param date the <code>CalendarDate</code> to be validated 397 * @return <code>true</code> if all the calendar fields are consistent, 398 * otherwise, <code>false</code> is returned. 399 * @exception NullPointerException if the specified 400 * <code>date</code> is <code>null</code> 401 */ validate(CalendarDate date)402 public abstract boolean validate(CalendarDate date); 403 404 /** 405 * Normalizes calendar fields in the specified 406 * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED 407 * undefined} fields are set to correct values. The actual 408 * normalization process is calendar system dependent. 409 * 410 * @param date the calendar date to be validated 411 * @return <code>true</code> if all fields have been normalized; 412 * <code>false</code> otherwise. 413 * @exception NullPointerException if the specified 414 * <code>date</code> is <code>null</code> 415 */ normalize(CalendarDate date)416 public abstract boolean normalize(CalendarDate date); 417 } 418