/* |
|
* Copyright (c) 2010, 2020, 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 |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package java.text; |
|
import java.util.Calendar; |
|
import java.util.StringJoiner; |
|
import static java.util.GregorianCalendar.*; |
|
/** |
|
* {@code CalendarBuilder} keeps field-value pairs for setting |
|
* the calendar fields of the given {@code Calendar}. It has the |
|
* {@link Calendar#FIELD_COUNT FIELD_COUNT}-th field for the week year |
|
* support. Also {@code ISO_DAY_OF_WEEK} is used to specify |
|
* {@code DAY_OF_WEEK} in the ISO day of week numbering. |
|
* |
|
* <p>{@code CalendarBuilder} retains the semantic of the pseudo |
|
* timestamp for fields. {@code CalendarBuilder} uses a single |
|
* int array combining fields[] and stamp[] of {@code Calendar}. |
|
* |
|
* @author Masayoshi Okutsu |
|
*/ |
|
class CalendarBuilder { |
|
/* |
|
* Pseudo time stamp constants used in java.util.Calendar |
|
*/ |
|
private static final int UNSET = 0; |
|
private static final int COMPUTED = 1; |
|
private static final int MINIMUM_USER_STAMP = 2; |
|
private static final int MAX_FIELD = FIELD_COUNT + 1; |
|
public static final int WEEK_YEAR = FIELD_COUNT; |
|
public static final int ISO_DAY_OF_WEEK = 1000; // pseudo field index |
|
// stamp[] (lower half) and field[] (upper half) combined |
|
private final int[] field; |
|
private int nextStamp; |
|
private int maxFieldIndex; |
|
CalendarBuilder() { |
|
field = new int[MAX_FIELD * 2]; |
|
nextStamp = MINIMUM_USER_STAMP; |
|
maxFieldIndex = -1; |
|
} |
|
CalendarBuilder set(int index, int value) { |
|
if (index == ISO_DAY_OF_WEEK) { |
|
index = DAY_OF_WEEK; |
|
value = toCalendarDayOfWeek(value); |
|
} |
|
field[index] = nextStamp++; |
|
field[MAX_FIELD + index] = value; |
|
if (index > maxFieldIndex && index < FIELD_COUNT) { |
|
maxFieldIndex = index; |
|
} |
|
return this; |
|
} |
|
CalendarBuilder addYear(int value) { |
|
field[MAX_FIELD + YEAR] += value; |
|
field[MAX_FIELD + WEEK_YEAR] += value; |
|
return this; |
|
} |
|
boolean isSet(int index) { |
|
if (index == ISO_DAY_OF_WEEK) { |
|
index = DAY_OF_WEEK; |
|
} |
|
return field[index] > UNSET; |
|
} |
|
CalendarBuilder clear(int index) { |
|
if (index == ISO_DAY_OF_WEEK) { |
|
index = DAY_OF_WEEK; |
|
} |
|
field[index] = UNSET; |
|
field[MAX_FIELD + index] = 0; |
|
return this; |
|
} |
|
Calendar establish(Calendar cal) { |
|
boolean weekDate = isSet(WEEK_YEAR) |
|
&& field[WEEK_YEAR] > field[YEAR]; |
|
if (weekDate && !cal.isWeekDateSupported()) { |
|
// Use YEAR instead |
|
if (!isSet(YEAR)) { |
|
set(YEAR, field[MAX_FIELD + WEEK_YEAR]); |
|
} |
|
weekDate = false; |
|
} |
|
cal.clear(); |
|
// Set the fields from the min stamp to the max stamp so that |
|
// the field resolution works in the Calendar. |
|
for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) { |
|
for (int index = 0; index <= maxFieldIndex; index++) { |
|
if (field[index] == stamp) { |
|
cal.set(index, field[MAX_FIELD + index]); |
|
break; |
|
} |
|
} |
|
} |
|
if (weekDate) { |
|
int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1; |
|
int dayOfWeek = isSet(DAY_OF_WEEK) ? |
|
field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek(); |
|
if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) { |
|
if (dayOfWeek >= 8) { |
|
dayOfWeek--; |
|
weekOfYear += dayOfWeek / 7; |
|
dayOfWeek = (dayOfWeek % 7) + 1; |
|
} else { |
|
while (dayOfWeek <= 0) { |
|
dayOfWeek += 7; |
|
weekOfYear--; |
|
} |
|
} |
|
dayOfWeek = toCalendarDayOfWeek(dayOfWeek); |
|
} |
|
cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek); |
|
} |
|
return cal; |
|
} |
|
public String toString() { |
|
StringJoiner sj = new StringJoiner(",", "CalendarBuilder:[", "]"); |
|
for (int i = 0; i < MAX_FIELD; i++) { |
|
if (isSet(i)) { |
|
sj.add(i + "=" + field[i] + ":" + field[MAX_FIELD + i]); |
|
} |
|
} |
|
return sj.toString(); |
|
} |
|
static int toISODayOfWeek(int calendarDayOfWeek) { |
|
return calendarDayOfWeek == SUNDAY ? 7 : calendarDayOfWeek - 1; |
|
} |
|
static int toCalendarDayOfWeek(int isoDayOfWeek) { |
|
if (!isValidDayOfWeek(isoDayOfWeek)) { |
|
// adjust later for lenient mode |
|
return isoDayOfWeek; |
|
} |
|
return isoDayOfWeek == 7 ? SUNDAY : isoDayOfWeek + 1; |
|
} |
|
static boolean isValidDayOfWeek(int dayOfWeek) { |
|
return dayOfWeek > 0 && dayOfWeek <= 7; |
|
} |
|
} |