| /* |
| * 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; |
| } |
| } |