1 /* 2 * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.text; 27 28 import java.util.Calendar; 29 import java.util.StringJoiner; 30 import static java.util.GregorianCalendar.*; 31 32 /** 33 * {@code CalendarBuilder} keeps field-value pairs for setting 34 * the calendar fields of the given {@code Calendar}. It has the 35 * {@link Calendar#FIELD_COUNT FIELD_COUNT}-th field for the week year 36 * support. Also {@code ISO_DAY_OF_WEEK} is used to specify 37 * {@code DAY_OF_WEEK} in the ISO day of week numbering. 38 * 39 * <p>{@code CalendarBuilder} retains the semantic of the pseudo 40 * timestamp for fields. {@code CalendarBuilder} uses a single 41 * int array combining fields[] and stamp[] of {@code Calendar}. 42 * 43 * @author Masayoshi Okutsu 44 */ 45 class CalendarBuilder { 46 /* 47 * Pseudo time stamp constants used in java.util.Calendar 48 */ 49 private static final int UNSET = 0; 50 private static final int COMPUTED = 1; 51 private static final int MINIMUM_USER_STAMP = 2; 52 53 private static final int MAX_FIELD = FIELD_COUNT + 1; 54 55 public static final int WEEK_YEAR = FIELD_COUNT; 56 public static final int ISO_DAY_OF_WEEK = 1000; // pseudo field index 57 58 // stamp[] (lower half) and field[] (upper half) combined 59 private final int[] field; 60 private int nextStamp; 61 private int maxFieldIndex; 62 CalendarBuilder()63 CalendarBuilder() { 64 field = new int[MAX_FIELD * 2]; 65 nextStamp = MINIMUM_USER_STAMP; 66 maxFieldIndex = -1; 67 } 68 set(int index, int value)69 CalendarBuilder set(int index, int value) { 70 if (index == ISO_DAY_OF_WEEK) { 71 index = DAY_OF_WEEK; 72 value = toCalendarDayOfWeek(value); 73 } 74 field[index] = nextStamp++; 75 field[MAX_FIELD + index] = value; 76 if (index > maxFieldIndex && index < FIELD_COUNT) { 77 maxFieldIndex = index; 78 } 79 return this; 80 } 81 addYear(int value)82 CalendarBuilder addYear(int value) { 83 field[MAX_FIELD + YEAR] += value; 84 field[MAX_FIELD + WEEK_YEAR] += value; 85 return this; 86 } 87 isSet(int index)88 boolean isSet(int index) { 89 if (index == ISO_DAY_OF_WEEK) { 90 index = DAY_OF_WEEK; 91 } 92 return field[index] > UNSET; 93 } 94 clear(int index)95 CalendarBuilder clear(int index) { 96 if (index == ISO_DAY_OF_WEEK) { 97 index = DAY_OF_WEEK; 98 } 99 field[index] = UNSET; 100 field[MAX_FIELD + index] = 0; 101 return this; 102 } 103 establish(Calendar cal)104 Calendar establish(Calendar cal) { 105 boolean weekDate = isSet(WEEK_YEAR) 106 && field[WEEK_YEAR] > field[YEAR]; 107 if (weekDate && !cal.isWeekDateSupported()) { 108 // Use YEAR instead 109 if (!isSet(YEAR)) { 110 set(YEAR, field[MAX_FIELD + WEEK_YEAR]); 111 } 112 weekDate = false; 113 } 114 115 cal.clear(); 116 // Set the fields from the min stamp to the max stamp so that 117 // the field resolution works in the Calendar. 118 for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) { 119 for (int index = 0; index <= maxFieldIndex; index++) { 120 if (field[index] == stamp) { 121 cal.set(index, field[MAX_FIELD + index]); 122 break; 123 } 124 } 125 } 126 127 if (weekDate) { 128 int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1; 129 int dayOfWeek = isSet(DAY_OF_WEEK) ? 130 field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek(); 131 if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) { 132 if (dayOfWeek >= 8) { 133 dayOfWeek--; 134 weekOfYear += dayOfWeek / 7; 135 dayOfWeek = (dayOfWeek % 7) + 1; 136 } else { 137 while (dayOfWeek <= 0) { 138 dayOfWeek += 7; 139 weekOfYear--; 140 } 141 } 142 dayOfWeek = toCalendarDayOfWeek(dayOfWeek); 143 } 144 cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek); 145 } 146 return cal; 147 } 148 toString()149 public String toString() { 150 StringJoiner sj = new StringJoiner(",", "CalendarBuilder:[", "]"); 151 for (int i = 0; i < MAX_FIELD; i++) { 152 if (isSet(i)) { 153 sj.add(i + "=" + field[i] + ":" + field[MAX_FIELD + i]); 154 } 155 } 156 return sj.toString(); 157 } 158 toISODayOfWeek(int calendarDayOfWeek)159 static int toISODayOfWeek(int calendarDayOfWeek) { 160 return calendarDayOfWeek == SUNDAY ? 7 : calendarDayOfWeek - 1; 161 } 162 toCalendarDayOfWeek(int isoDayOfWeek)163 static int toCalendarDayOfWeek(int isoDayOfWeek) { 164 if (!isValidDayOfWeek(isoDayOfWeek)) { 165 // adjust later for lenient mode 166 return isoDayOfWeek; 167 } 168 return isoDayOfWeek == 7 ? SUNDAY : isoDayOfWeek + 1; 169 } 170 isValidDayOfWeek(int dayOfWeek)171 static boolean isValidDayOfWeek(int dayOfWeek) { 172 return dayOfWeek > 0 && dayOfWeek <= 7; 173 } 174 } 175