8316435: sun.util.calendar.CalendarSystem subclassing should be restricted

Reviewed-by: naoto
This commit is contained in:
Justin Lu 2023-09-21 21:31:07 +00:00
parent 1100dbc6b2
commit 496264c1f9
9 changed files with 142 additions and 48 deletions

@ -44,7 +44,8 @@ import java.util.TimeZone;
* @since 1.5
*/
public abstract class AbstractCalendar extends CalendarSystem {
public sealed abstract class AbstractCalendar extends CalendarSystem
permits BaseCalendar {
// The constants assume no leap seconds support.
static final int SECOND_IN_MILLIS = 1000;
@ -60,6 +61,7 @@ public abstract class AbstractCalendar extends CalendarSystem {
protected AbstractCalendar() {
}
@Override
public Era getEra(String eraName) {
if (eras != null) {
for (Era era : eras) {
@ -71,6 +73,7 @@ public abstract class AbstractCalendar extends CalendarSystem {
return null;
}
@Override
public Era[] getEras() {
Era[] e = null;
if (eras != null) {
@ -84,19 +87,23 @@ public abstract class AbstractCalendar extends CalendarSystem {
this.eras = eras;
}
@Override
public CalendarDate getCalendarDate() {
return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
}
@Override
public CalendarDate getCalendarDate(long millis) {
return getCalendarDate(millis, newCalendarDate());
}
@Override
public CalendarDate getCalendarDate(long millis, TimeZone zone) {
CalendarDate date = newCalendarDate(zone);
return getCalendarDate(millis, date);
}
@Override
public CalendarDate getCalendarDate(long millis, CalendarDate date) {
int ms = 0; // time of day
int zoneOffset = 0;
@ -156,6 +163,7 @@ public abstract class AbstractCalendar extends CalendarSystem {
return date;
}
@Override
public long getTime(CalendarDate date) {
long gd = getFixedDate(date);
long ms = (gd - EPOCH_OFFSET) * DAY_IN_MILLIS + getTimeOfDay(date);
@ -232,6 +240,7 @@ public abstract class AbstractCalendar extends CalendarSystem {
protected abstract boolean isLeapYear(CalendarDate date);
@Override
public CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date) {
CalendarDate ndate = (CalendarDate) date.clone();
normalize(ndate);

@ -36,7 +36,8 @@ import java.util.TimeZone;
* @since 1.5
*/
public abstract class BaseCalendar extends AbstractCalendar {
public sealed abstract class BaseCalendar extends AbstractCalendar
permits Gregorian, JulianCalendar, LocalGregorianCalendar {
public static final int JANUARY = 1;
public static final int FEBRUARY = 2;
@ -140,7 +141,8 @@ public abstract class BaseCalendar extends AbstractCalendar {
744365, // 2039
};
public abstract static class Date extends CalendarDate {
public sealed abstract static class Date extends CalendarDate
permits Gregorian.Date, ImmutableGregorianDate, JulianCalendar.Date, LocalGregorianCalendar.Date {
protected Date() {
super();
}
@ -188,6 +190,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
}
}
@Override
public boolean validate(CalendarDate date) {
Date bdate = (Date) date;
if (bdate.isNormalized()) {
@ -214,6 +217,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
return true;
}
@Override
public boolean normalize(CalendarDate date) {
if (date.isNormalized()) {
return true;
@ -303,6 +307,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
* @throws ClassCastException if the specified date is not a
* {@link BaseCalendar.Date}
*/
@Override
public int getYearLength(CalendarDate date) {
return isLeapYear(((Date)date).getNormalizedYear()) ? 366 : 365;
}
@ -318,6 +323,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
// 12/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1 12/1
= { -30, 0, 31, 59+1, 90+1,120+1,151+1,181+1,212+1,243+1, 273+1, 304+1, 334+1};
@Override
public int getMonthLength(CalendarDate date) {
Date gdate = (Date) date;
int month = gdate.getMonth();
@ -349,6 +355,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
}
// protected
@Override
public long getFixedDate(CalendarDate date) {
if (!date.isNormalized()) {
normalizeMonth(date);
@ -415,6 +422,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
* {@code CalendarDate}.
*/
// should be 'protected'
@Override
public void getCalendarDateFromFixedDate(CalendarDate date,
long fixedDate) {
Date gdate = (Date) date;
@ -473,7 +481,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
return getDayOfWeekFromFixedDate(fixedDate);
}
public static final int getDayOfWeekFromFixedDate(long fixedDate) {
public static int getDayOfWeekFromFixedDate(long fixedDate) {
// The fixed day 1 (January 1, 1 Gregorian) is Monday.
if (fixedDate >= 0) {
return (int)(fixedDate % 7) + SUNDAY;
@ -525,6 +533,7 @@ public abstract class BaseCalendar extends AbstractCalendar {
* false otherwise.
* @see CalendarUtils#isGregorianLeapYear
*/
@Override
protected boolean isLeapYear(CalendarDate date) {
return isLeapYear(((Date)date).getNormalizedYear());
}

@ -59,7 +59,8 @@ import java.util.TimeZone;
* @author Masayoshi Okutsu
* @since 1.5
*/
public abstract class CalendarDate implements Cloneable {
public sealed abstract class CalendarDate implements Cloneable
permits BaseCalendar.Date {
public static final int FIELD_UNDEFINED = Integer.MIN_VALUE;
public static final long TIME_UNDEFINED = Long.MIN_VALUE;
@ -340,6 +341,7 @@ public abstract class CalendarDate implements Cloneable {
&& zoneOffset == that.zoneOffset);
}
@Override
public int hashCode() {
// a pseudo (local standard) time stamp value in milliseconds
// from the Epoch, assuming Gregorian calendar fields.
@ -362,6 +364,7 @@ public abstract class CalendarDate implements Cloneable {
*
* @return a copy of this <code>CalendarDate</code>
*/
@Override
public Object clone() {
try {
return super.clone();
@ -380,6 +383,7 @@ public abstract class CalendarDate implements Cloneable {
*
* @see java.text.SimpleDateFormat
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
CalendarUtils.sprintf0d(sb, year, 4).append('-');

@ -65,7 +65,7 @@ import java.util.concurrent.ConcurrentMap;
* @since 1.5
*/
public abstract class CalendarSystem {
public sealed abstract class CalendarSystem permits AbstractCalendar {
/////////////////////// Calendar Factory Methods /////////////////////////

@ -25,7 +25,10 @@
package sun.util.calendar;
public class CalendarUtils {
public final class CalendarUtils {
// Utility class should not be instantiated
private CalendarUtils() {}
/**
* Returns whether the specified year is a leap year in the Gregorian
@ -36,9 +39,9 @@ public class CalendarUtils {
* calendar system.
* @see CalendarDate#isLeapYear
*/
public static final boolean isGregorianLeapYear(int gregorianYear) {
return (((gregorianYear % 4) == 0)
&& (((gregorianYear % 100) != 0) || ((gregorianYear % 400) == 0)));
public static boolean isGregorianLeapYear(int gregorianYear) {
return (((gregorianYear % 4) == 0) && (((gregorianYear % 100) != 0)
|| ((gregorianYear % 400) == 0)));
}
/**
@ -51,7 +54,7 @@ public class CalendarUtils {
* calendar system.
* @see CalendarDate#isLeapYear
*/
public static final boolean isJulianLeapYear(int normalizedJulianYear) {
public static boolean isJulianLeapYear(int normalizedJulianYear) {
return (normalizedJulianYear % 4) == 0;
}
@ -64,7 +67,7 @@ public class CalendarUtils {
* @param d a divisor that must be greater than 0
* @return the floor of the quotient
*/
public static final long floorDivide(long n, long d) {
public static long floorDivide(long n, long d) {
return ((n >= 0) ?
(n / d) : (((n + 1L) / d) - 1L));
}
@ -78,7 +81,7 @@ public class CalendarUtils {
* @param d a divisor that must be greater than 0
* @return the floor of the quotient
*/
public static final int floorDivide(int n, int d) {
public static int floorDivide(int n, int d) {
return ((n >= 0) ?
(n / d) : (((n + 1) / d) - 1));
}
@ -96,7 +99,7 @@ public class CalendarUtils {
* <code>mod(n, d)</code> is returned.
* @return the floor of the quotient.
*/
public static final int floorDivide(int n, int d, int[] r) {
public static int floorDivide(int n, int d, int[] r) {
if (n >= 0) {
r[0] = n % d;
return n / d;
@ -106,20 +109,20 @@ public class CalendarUtils {
return q;
}
public static final long mod(long x, long y) {
public static long mod(long x, long y) {
return (x - y * floorDivide(x, y));
}
public static final int mod(int x, int y) {
public static int mod(int x, int y) {
return (x - y * floorDivide(x, y));
}
public static final int amod(int x, int y) {
public static int amod(int x, int y) {
int z = mod(x, y);
return (z == 0) ? y : z;
}
public static final long amod(long x, long y) {
public static long amod(long x, long y) {
long z = mod(x, y);
return (z == 0) ? y : z;
}
@ -127,7 +130,7 @@ public class CalendarUtils {
/**
* Mimics sprintf(buf, "%0*d", decaimal, width).
*/
public static final StringBuilder sprintf0d(StringBuilder sb, int value, int width) {
public static StringBuilder sprintf0d(StringBuilder sb, int value, int width) {
long d = value;
if (d < 0) {
sb.append('-');
@ -146,7 +149,7 @@ public class CalendarUtils {
return sb;
}
public static final StringBuffer sprintf0d(StringBuffer sb, int value, int width) {
public static StringBuffer sprintf0d(StringBuffer sb, int value, int width) {
long d = value;
if (d < 0) {
sb.append('-');

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -34,21 +34,23 @@ import java.util.TimeZone;
* @since 1.5
*/
public class Gregorian extends BaseCalendar {
public final class Gregorian extends BaseCalendar {
static class Date extends BaseCalendar.Date {
protected Date() {
static final class Date extends BaseCalendar.Date {
Date() {
super();
}
protected Date(TimeZone zone) {
Date(TimeZone zone) {
super(zone);
}
@Override
public int getNormalizedYear() {
return getYear();
}
@Override
public void setNormalizedYear(int normalizedYear) {
setYear(normalizedYear);
}
@ -57,30 +59,37 @@ public class Gregorian extends BaseCalendar {
Gregorian() {
}
@Override
public String getName() {
return "gregorian";
}
@Override
public Date getCalendarDate() {
return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
}
@Override
public Date getCalendarDate(long millis) {
return getCalendarDate(millis, newCalendarDate());
}
@Override
public Date getCalendarDate(long millis, CalendarDate date) {
return (Date) super.getCalendarDate(millis, date);
}
@Override
public Date getCalendarDate(long millis, TimeZone zone) {
return getCalendarDate(millis, newCalendarDate(zone));
}
@Override
public Date newCalendarDate() {
return new Date();
}
@Override
public Date newCalendarDate(TimeZone zone) {
return new Date(zone);
}

@ -25,192 +25,238 @@
package sun.util.calendar;
import java.util.Objects;
import java.util.TimeZone;
class ImmutableGregorianDate extends BaseCalendar.Date {
/*
* This class is immutable, and thus any methods from the base classes
* that can modify the state are overridden to throw an exception.
*/
final class ImmutableGregorianDate extends BaseCalendar.Date {
private final BaseCalendar.Date date;
ImmutableGregorianDate(BaseCalendar.Date date) {
if (date == null) {
throw new NullPointerException();
}
this.date = date;
this.date = Objects.requireNonNull(date);
}
@Override
public Era getEra() {
return date.getEra();
}
@Override
public CalendarDate setEra(Era era) {
unsupported(); return this;
}
@Override
public int getYear() {
return date.getYear();
}
@Override
public CalendarDate setYear(int year) {
unsupported(); return this;
}
@Override
public CalendarDate addYear(int n) {
unsupported(); return this;
}
@Override
public boolean isLeapYear() {
return date.isLeapYear();
}
@Override
void setLeapYear(boolean leapYear) {
unsupported();
}
@Override
public int getMonth() {
return date.getMonth();
}
@Override
public CalendarDate setMonth(int month) {
unsupported(); return this;
}
@Override
public CalendarDate addMonth(int n) {
unsupported(); return this;
}
@Override
public int getDayOfMonth() {
return date.getDayOfMonth();
}
@Override
public CalendarDate setDayOfMonth(int date) {
unsupported(); return this;
}
@Override
public int getDayOfWeek() {
return date.getDayOfWeek();
}
@Override
public int getHours() {
return date.getHours();
}
@Override
public CalendarDate setHours(int hours) {
unsupported(); return this;
}
@Override
public CalendarDate addHours(int n) {
unsupported(); return this;
}
@Override
public int getMinutes() {
return date.getMinutes();
}
@Override
public CalendarDate setMinutes(int minutes) {
unsupported(); return this;
}
@Override
public int getSeconds() {
return date.getSeconds();
}
@Override
public CalendarDate setSeconds(int seconds) {
unsupported(); return this;
}
@Override
public int getMillis() {
return date.getMillis();
}
@Override
public CalendarDate setMillis(int millis) {
unsupported(); return this;
}
@Override
public long getTimeOfDay() {
return date.getTimeOfDay();
}
@Override
public CalendarDate setDate(int year, int month, int dayOfMonth) {
unsupported(); return this;
}
@Override
public CalendarDate setTimeOfDay(int hours, int minutes, int seconds, int millis) {
unsupported(); return this;
}
@Override
protected void setTimeOfDay(long fraction) {
unsupported();
}
@Override
public boolean isNormalized() {
return date.isNormalized();
}
@Override
public boolean isDaylightTime() {
return date.isDaylightTime();
}
@Override
public TimeZone getZone() {
return date.getZone();
}
@Override
public CalendarDate setZone(TimeZone zoneinfo) {
unsupported(); return this;
}
@Override
public boolean isSameDate(CalendarDate date) {
return date.isSameDate(date);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ImmutableGregorianDate)) {
if (obj instanceof ImmutableGregorianDate igd) {
return date.equals(igd.date);
} else {
return false;
}
return date.equals(((ImmutableGregorianDate) obj).date);
}
@Override
public int hashCode() {
return date.hashCode();
}
@Override
public Object clone() {
return super.clone();
}
@Override
public String toString() {
return date.toString();
}
@Override
protected void setDayOfWeek(int dayOfWeek) {
unsupported();
}
@Override
protected void setNormalized(boolean normalized) {
unsupported();
}
@Override
public int getZoneOffset() {
return date.getZoneOffset();
}
@Override
protected void setZoneOffset(int offset) {
unsupported();
}
@Override
public int getDaylightSaving() {
return date.getDaylightSaving();
}
@Override
protected void setDaylightSaving(int daylightSaving) {
unsupported();
}
@Override
public int getNormalizedYear() {
return date.getNormalizedYear();
}
@Override
public void setNormalizedYear(int normalizedYear) {
unsupported();
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -33,7 +33,7 @@ import java.util.TimeZone;
* @author Masayoshi Okutsu
* @since 1.5
*/
public class JulianCalendar extends BaseCalendar {
public final class JulianCalendar extends BaseCalendar {
private static final int BCE = 0;
private static final int CE = 1;
@ -44,17 +44,18 @@ public class JulianCalendar extends BaseCalendar {
};
private static final int JULIAN_EPOCH = -1;
private static class Date extends BaseCalendar.Date {
protected Date() {
static final class Date extends BaseCalendar.Date {
Date() {
super();
setCache(1, -1L, 365); // January 1, 1 CE (Julian)
}
protected Date(TimeZone zone) {
Date(TimeZone zone) {
super(zone);
setCache(1, -1L, 365); // January 1, 1 CE (Julian)
}
@Override
public Date setEra(Era era) {
if (era == null) {
throw new NullPointerException();
@ -66,10 +67,11 @@ public class JulianCalendar extends BaseCalendar {
return this;
}
protected void setKnownEra(Era era) {
void setKnownEra(Era era) {
super.setEra(era);
}
@Override
public int getNormalizedYear() {
if (getEra() == eras[BCE]) {
return 1 - getYear();
@ -81,6 +83,7 @@ public class JulianCalendar extends BaseCalendar {
// normalized years. This differs from "Calendrical
// Calculations" in which the numbering is ..., -2, -1, 1, 2,
// ...
@Override
public void setNormalizedYear(int year) {
if (year <= 0) {
setYear(1 - year);
@ -91,6 +94,7 @@ public class JulianCalendar extends BaseCalendar {
}
}
@Override
public String toString() {
String time = super.toString();
time = time.substring(time.indexOf('T'));
@ -114,30 +118,37 @@ public class JulianCalendar extends BaseCalendar {
setEras(eras);
}
@Override
public String getName() {
return "julian";
}
@Override
public Date getCalendarDate() {
return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
}
@Override
public Date getCalendarDate(long millis) {
return getCalendarDate(millis, newCalendarDate());
}
@Override
public Date getCalendarDate(long millis, CalendarDate date) {
return (Date) super.getCalendarDate(millis, date);
}
@Override
public Date getCalendarDate(long millis, TimeZone zone) {
return getCalendarDate(millis, newCalendarDate(zone));
}
@Override
public Date newCalendarDate() {
return new Date();
}
@Override
public Date newCalendarDate(TimeZone zone) {
return new Date(zone);
}
@ -145,6 +156,7 @@ public class JulianCalendar extends BaseCalendar {
/**
* @param jyear normalized Julian year
*/
@Override
public long getFixedDate(int jyear, int month, int dayOfMonth, BaseCalendar.Date cache) {
boolean isJan1 = month == JANUARY && dayOfMonth == 1;
@ -182,6 +194,7 @@ public class JulianCalendar extends BaseCalendar {
return days;
}
@Override
public void getCalendarDateFromFixedDate(CalendarDate date, long fixedDate) {
Date jdate = (Date) date;
long fd = 4 * (fixedDate - JULIAN_EPOCH) + 1464;
@ -216,18 +229,18 @@ public class JulianCalendar extends BaseCalendar {
/**
* Returns the normalized Julian year number of the given fixed date.
*/
@Override
public int getYearFromFixedDate(long fixedDate) {
int year = (int) CalendarUtils.floorDivide(4 * (fixedDate - JULIAN_EPOCH) + 1464, 1461);
return year;
return (int) CalendarUtils.floorDivide(4 * (fixedDate - JULIAN_EPOCH) + 1464, 1461);
}
@Override
public int getDayOfWeek(CalendarDate date) {
// TODO: should replace this with a faster calculation, such
// as cache table lookup
long fixedDate = getFixedDate(date);
return getDayOfWeekFromFixedDate(fixedDate);
// TODO: should replace with faster calculation, e.g. cache table lookup
return getDayOfWeekFromFixedDate(getFixedDate(date));
}
@Override
boolean isLeapYear(int jyear) {
return CalendarUtils.isJulianLeapYear(jyear);
}

@ -36,7 +36,7 @@ import sun.security.action.GetPropertyAction;
* @since 1.6
*/
public class LocalGregorianCalendar extends BaseCalendar {
public final class LocalGregorianCalendar extends BaseCalendar {
private static final Era[] JAPANESE_ERAS = {
new Era("Meiji", "M", -3218832000000L, true),
new Era("Taisho", "T", -1812153600000L, true),
@ -63,13 +63,14 @@ public class LocalGregorianCalendar extends BaseCalendar {
private String name;
private Era[] eras;
public static class Date extends BaseCalendar.Date {
// Used within java.time and java.util
public static final class Date extends BaseCalendar.Date {
protected Date() {
Date() {
super();
}
protected Date(TimeZone zone) {
Date(TimeZone zone) {
super(zone);
}