src/net/fortuna/ical4j/model/Recur.java

changeset 3
73bdfa70b04e
parent 0
fb9019fb1bf7
equal deleted inserted replaced
0:2b633facb90b 1:abfd0876272b
42 import java.util.Map; 42 import java.util.Map;
43 import java.util.NoSuchElementException; 43 import java.util.NoSuchElementException;
44 import java.util.StringTokenizer; 44 import java.util.StringTokenizer;
45 45
46 import net.fortuna.ical4j.model.parameter.Value; 46 import net.fortuna.ical4j.model.parameter.Value;
47 import net.fortuna.ical4j.util.CompatibilityHints;
47 import net.fortuna.ical4j.util.Configurator; 48 import net.fortuna.ical4j.util.Configurator;
48 import net.fortuna.ical4j.util.Dates; 49 import net.fortuna.ical4j.util.Dates;
49 50
50 import org.apache.commons.logging.Log; 51 import org.apache.commons.logging.Log;
51 import org.apache.commons.logging.LogFactory; 52 import org.apache.commons.logging.LogFactory;
169 private NumberList monthList; 170 private NumberList monthList;
170 171
171 private NumberList setPosList; 172 private NumberList setPosList;
172 173
173 private String weekStartDay; 174 private String weekStartDay;
175
176 private int calendarWeekStartDay;
174 177
175 private Map experimentalValues = new HashMap(); 178 private Map experimentalValues = new HashMap();
176 179
177 // Calendar field we increment based on frequency. 180 // Calendar field we increment based on frequency.
178 private int calIncField; 181 private int calIncField;
179 182
180 /** 183 /**
181 * Default constructor. 184 * Default constructor.
182 */ 185 */
183 public Recur() { 186 public Recur() {
187 // default week start is Monday per RFC5545
188 calendarWeekStartDay = Calendar.MONDAY;
184 } 189 }
185 190
186 /** 191 /**
187 * Constructs a new instance from the specified string value. 192 * Constructs a new instance from the specified string value.
188 * @param aValue a string representation of a recurrence. 193 * @param aValue a string representation of a recurrence.
189 * @throws ParseException thrown when the specified string contains an invalid representation of an UNTIL date value 194 * @throws ParseException thrown when the specified string contains an invalid representation of an UNTIL date value
190 */ 195 */
191 public Recur(final String aValue) throws ParseException { 196 public Recur(final String aValue) throws ParseException {
197 // default week start is Monday per RFC5545
198 calendarWeekStartDay = Calendar.MONDAY;
192 final StringTokenizer t = new StringTokenizer(aValue, ";="); 199 final StringTokenizer t = new StringTokenizer(aValue, ";=");
193 while (t.hasMoreTokens()) { 200 while (t.hasMoreTokens()) {
194 final String token = t.nextToken(); 201 final String token = t.nextToken();
195 if (FREQ.equals(token)) { 202 if (FREQ.equals(token)) {
196 frequency = nextToken(t, token); 203 frequency = nextToken(t, token);
239 else if (BYSETPOS.equals(token)) { 246 else if (BYSETPOS.equals(token)) {
240 setPosList = new NumberList(nextToken(t, token), 1, 366, true); 247 setPosList = new NumberList(nextToken(t, token), 1, 366, true);
241 } 248 }
242 else if (WKST.equals(token)) { 249 else if (WKST.equals(token)) {
243 weekStartDay = nextToken(t, token); 250 weekStartDay = nextToken(t, token);
244 } 251 calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay));
245 // assume experimental value.. 252 }
246 else { 253 else {
247 experimentalValues.put(token, nextToken(t, token)); 254 if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
255 // assume experimental value..
256 experimentalValues.put(token, nextToken(t, token));
257 }
258 else {
259 throw new IllegalArgumentException("Invalid recurrence rule part: " +
260 token + "=" + nextToken(t, token));
261 }
248 } 262 }
249 } 263 }
250 validateFrequency(); 264 validateFrequency();
251 } 265 }
252 266
262 /** 276 /**
263 * @param frequency a recurrence frequency string 277 * @param frequency a recurrence frequency string
264 * @param until maximum recurrence date 278 * @param until maximum recurrence date
265 */ 279 */
266 public Recur(final String frequency, final Date until) { 280 public Recur(final String frequency, final Date until) {
281 // default week start is Monday per RFC5545
282 calendarWeekStartDay = Calendar.MONDAY;
267 this.frequency = frequency; 283 this.frequency = frequency;
268 this.until = until; 284 this.until = until;
269 validateFrequency(); 285 validateFrequency();
270 } 286 }
271 287
272 /** 288 /**
273 * @param frequency a recurrence frequency string 289 * @param frequency a recurrence frequency string
274 * @param count maximum recurrence count 290 * @param count maximum recurrence count
275 */ 291 */
276 public Recur(final String frequency, final int count) { 292 public Recur(final String frequency, final int count) {
293 // default week start is Monday per RFC5545
294 calendarWeekStartDay = Calendar.MONDAY;
277 this.frequency = frequency; 295 this.frequency = frequency;
278 this.count = count; 296 this.count = count;
279 validateFrequency(); 297 validateFrequency();
280 } 298 }
281 299
414 /** 432 /**
415 * @param weekStartDay The weekStartDay to set. 433 * @param weekStartDay The weekStartDay to set.
416 */ 434 */
417 public final void setWeekStartDay(final String weekStartDay) { 435 public final void setWeekStartDay(final String weekStartDay) {
418 this.weekStartDay = weekStartDay; 436 this.weekStartDay = weekStartDay;
437 if (weekStartDay != null) {
438 calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay));
439 }
419 } 440 }
420 441
421 /** 442 /**
422 * {@inheritDoc} 443 * {@inheritDoc}
423 */ 444 */
576 } 597 }
577 else { 598 else {
578 dates.setTimeZone(((DateTime) seed).getTimeZone()); 599 dates.setTimeZone(((DateTime) seed).getTimeZone());
579 } 600 }
580 } 601 }
581 final Calendar cal = Dates.getCalendarInstance(seed); 602 final Calendar cal = getCalendarInstance(seed, true);
582 cal.setTime(seed);
583 603
584 // optimize the start time for selecting candidates 604 // optimize the start time for selecting candidates
585 // (only applicable where a COUNT is not specified) 605 // (only applicable where a COUNT is not specified)
586 if (getCount() < 1) { 606 if (getCount() < 1) {
587 final Calendar seededCal = (Calendar) cal.clone(); 607 final Calendar seededCal = (Calendar) cal.clone();
668 * @param seed the start date of this Recurrence's first instance 688 * @param seed the start date of this Recurrence's first instance
669 * @param startDate the date to start the search 689 * @param startDate the date to start the search
670 */ 690 */
671 public final Date getNextDate(final Date seed, final Date startDate) { 691 public final Date getNextDate(final Date seed, final Date startDate) {
672 692
673 final Calendar cal = Dates.getCalendarInstance(seed); 693 final Calendar cal = getCalendarInstance(seed, true);
674 cal.setTime(seed);
675 694
676 // optimize the start time for selecting candidates 695 // optimize the start time for selecting candidates
677 // (only applicable where a COUNT is not specified) 696 // (only applicable where a COUNT is not specified)
678 if (getCount() < 1) { 697 if (getCount() < 1) {
679 final Calendar seededCal = (Calendar) cal.clone(); 698 final Calendar seededCal = (Calendar) cal.clone();
855 return dates; 874 return dates;
856 } 875 }
857 final DateList monthlyDates = getDateListInstance(dates); 876 final DateList monthlyDates = getDateListInstance(dates);
858 for (final Iterator i = dates.iterator(); i.hasNext();) { 877 for (final Iterator i = dates.iterator(); i.hasNext();) {
859 final Date date = (Date) i.next(); 878 final Date date = (Date) i.next();
860 final Calendar cal = Dates.getCalendarInstance(date); 879 final Calendar cal = getCalendarInstance(date, true);
861 cal.setTime(date); 880
862 for (final Iterator j = getMonthList().iterator(); j.hasNext();) { 881 for (final Iterator j = getMonthList().iterator(); j.hasNext();) {
863 final Integer month = (Integer) j.next(); 882 final Integer month = (Integer) j.next();
864 // Java months are zero-based.. 883 // Java months are zero-based..
865 // cal.set(Calendar.MONTH, month.intValue() - 1); 884 // cal.set(Calendar.MONTH, month.intValue() - 1);
866 cal.roll(Calendar.MONTH, (month.intValue() - 1) - cal.get(Calendar.MONTH)); 885 cal.roll(Calendar.MONTH, (month.intValue() - 1) - cal.get(Calendar.MONTH));
881 return dates; 900 return dates;
882 } 901 }
883 final DateList weekNoDates = getDateListInstance(dates); 902 final DateList weekNoDates = getDateListInstance(dates);
884 for (final Iterator i = dates.iterator(); i.hasNext();) { 903 for (final Iterator i = dates.iterator(); i.hasNext();) {
885 final Date date = (Date) i.next(); 904 final Date date = (Date) i.next();
886 final Calendar cal = Dates.getCalendarInstance(date); 905 final Calendar cal = getCalendarInstance(date, true);
887 cal.setTime(date);
888 for (final Iterator j = getWeekNoList().iterator(); j.hasNext();) { 906 for (final Iterator j = getWeekNoList().iterator(); j.hasNext();) {
889 final Integer weekNo = (Integer) j.next(); 907 final Integer weekNo = (Integer) j.next();
890 cal.set(Calendar.WEEK_OF_YEAR, Dates.getAbsWeekNo(cal.getTime(), weekNo.intValue())); 908 cal.set(Calendar.WEEK_OF_YEAR, Dates.getAbsWeekNo(cal.getTime(), weekNo.intValue()));
891 weekNoDates.add(Dates.getInstance(cal.getTime(), weekNoDates.getType())); 909 weekNoDates.add(Dates.getInstance(cal.getTime(), weekNoDates.getType()));
892 } 910 }
905 return dates; 923 return dates;
906 } 924 }
907 final DateList yearDayDates = getDateListInstance(dates); 925 final DateList yearDayDates = getDateListInstance(dates);
908 for (final Iterator i = dates.iterator(); i.hasNext();) { 926 for (final Iterator i = dates.iterator(); i.hasNext();) {
909 final Date date = (Date) i.next(); 927 final Date date = (Date) i.next();
910 final Calendar cal = Dates.getCalendarInstance(date); 928 final Calendar cal = getCalendarInstance(date, true);
911 cal.setTime(date);
912 for (final Iterator j = getYearDayList().iterator(); j.hasNext();) { 929 for (final Iterator j = getYearDayList().iterator(); j.hasNext();) {
913 final Integer yearDay = (Integer) j.next(); 930 final Integer yearDay = (Integer) j.next();
914 cal.set(Calendar.DAY_OF_YEAR, Dates.getAbsYearDay(cal.getTime(), yearDay.intValue())); 931 cal.set(Calendar.DAY_OF_YEAR, Dates.getAbsYearDay(cal.getTime(), yearDay.intValue()));
915 yearDayDates.add(Dates.getInstance(cal.getTime(), yearDayDates.getType())); 932 yearDayDates.add(Dates.getInstance(cal.getTime(), yearDayDates.getType()));
916 } 933 }
929 return dates; 946 return dates;
930 } 947 }
931 final DateList monthDayDates = getDateListInstance(dates); 948 final DateList monthDayDates = getDateListInstance(dates);
932 for (final Iterator i = dates.iterator(); i.hasNext();) { 949 for (final Iterator i = dates.iterator(); i.hasNext();) {
933 final Date date = (Date) i.next(); 950 final Date date = (Date) i.next();
934 final Calendar cal = Dates.getCalendarInstance(date); 951 final Calendar cal = getCalendarInstance(date, false);
935 cal.setLenient(false);
936 cal.setTime(date);
937 for (final Iterator j = getMonthDayList().iterator(); j.hasNext();) { 952 for (final Iterator j = getMonthDayList().iterator(); j.hasNext();) {
938 final Integer monthDay = (Integer) j.next(); 953 final Integer monthDay = (Integer) j.next();
939 try { 954 try {
940 cal.set(Calendar.DAY_OF_MONTH, Dates.getAbsMonthDay(cal.getTime(), monthDay.intValue())); 955 cal.set(Calendar.DAY_OF_MONTH, Dates.getAbsMonthDay(cal.getTime(), monthDay.intValue()));
941 monthDayDates.add(Dates.getInstance(cal.getTime(), monthDayDates.getType())); 956 monthDayDates.add(Dates.getInstance(cal.getTime(), monthDayDates.getType()));
967 for (final Iterator j = getDayList().iterator(); j.hasNext();) { 982 for (final Iterator j = getDayList().iterator(); j.hasNext();) {
968 final WeekDay weekDay = (WeekDay) j.next(); 983 final WeekDay weekDay = (WeekDay) j.next();
969 // if BYYEARDAY or BYMONTHDAY is specified filter existing 984 // if BYYEARDAY or BYMONTHDAY is specified filter existing
970 // list.. 985 // list..
971 if (!getYearDayList().isEmpty() || !getMonthDayList().isEmpty()) { 986 if (!getYearDayList().isEmpty() || !getMonthDayList().isEmpty()) {
972 final Calendar cal = Dates.getCalendarInstance(date); 987 final Calendar cal = getCalendarInstance(date, true);
973 cal.setTime(date);
974 if (weekDay.equals(WeekDay.getWeekDay(cal))) { 988 if (weekDay.equals(WeekDay.getWeekDay(cal))) {
975 weekDayDates.add(date); 989 weekDayDates.add(date);
976 } 990 }
977 } 991 }
978 else { 992 else {
989 * @param date 1003 * @param date
990 * @param weekDay 1004 * @param weekDay
991 * @return 1005 * @return
992 */ 1006 */
993 private List getAbsWeekDays(final Date date, final Value type, final WeekDay weekDay) { 1007 private List getAbsWeekDays(final Date date, final Value type, final WeekDay weekDay) {
994 final Calendar cal = Dates.getCalendarInstance(date); 1008 final Calendar cal = getCalendarInstance(date, true);
995 // default week start is Monday per RFC5545
996 int calendarWeekStartDay = Calendar.MONDAY;
997 if (weekStartDay != null) {
998 calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay));
999 }
1000 cal.setFirstDayOfWeek(calendarWeekStartDay);
1001 cal.setTime(date);
1002
1003 final DateList days = new DateList(type); 1009 final DateList days = new DateList(type);
1004 if (date instanceof DateTime) { 1010 if (date instanceof DateTime) {
1005 if (((DateTime) date).isUtc()) { 1011 if (((DateTime) date).isUtc()) {
1006 days.setUtc(true); 1012 days.setUtc(true);
1007 } 1013 }
1093 return dates; 1099 return dates;
1094 } 1100 }
1095 final DateList hourlyDates = getDateListInstance(dates); 1101 final DateList hourlyDates = getDateListInstance(dates);
1096 for (final Iterator i = dates.iterator(); i.hasNext();) { 1102 for (final Iterator i = dates.iterator(); i.hasNext();) {
1097 final Date date = (Date) i.next(); 1103 final Date date = (Date) i.next();
1098 final Calendar cal = Dates.getCalendarInstance(date); 1104 final Calendar cal = getCalendarInstance(date, true);
1099 cal.setTime(date);
1100 for (final Iterator j = getHourList().iterator(); j.hasNext();) { 1105 for (final Iterator j = getHourList().iterator(); j.hasNext();) {
1101 final Integer hour = (Integer) j.next(); 1106 final Integer hour = (Integer) j.next();
1102 cal.set(Calendar.HOUR_OF_DAY, hour.intValue()); 1107 cal.set(Calendar.HOUR_OF_DAY, hour.intValue());
1103 hourlyDates.add(Dates.getInstance(cal.getTime(), hourlyDates.getType())); 1108 hourlyDates.add(Dates.getInstance(cal.getTime(), hourlyDates.getType()));
1104 } 1109 }
1117 return dates; 1122 return dates;
1118 } 1123 }
1119 final DateList minutelyDates = getDateListInstance(dates); 1124 final DateList minutelyDates = getDateListInstance(dates);
1120 for (final Iterator i = dates.iterator(); i.hasNext();) { 1125 for (final Iterator i = dates.iterator(); i.hasNext();) {
1121 final Date date = (Date) i.next(); 1126 final Date date = (Date) i.next();
1122 final Calendar cal = Dates.getCalendarInstance(date); 1127 final Calendar cal = getCalendarInstance(date, true);
1123 cal.setTime(date);
1124 for (final Iterator j = getMinuteList().iterator(); j.hasNext();) { 1128 for (final Iterator j = getMinuteList().iterator(); j.hasNext();) {
1125 final Integer minute = (Integer) j.next(); 1129 final Integer minute = (Integer) j.next();
1126 cal.set(Calendar.MINUTE, minute.intValue()); 1130 cal.set(Calendar.MINUTE, minute.intValue());
1127 minutelyDates.add(Dates.getInstance(cal.getTime(), minutelyDates.getType())); 1131 minutelyDates.add(Dates.getInstance(cal.getTime(), minutelyDates.getType()));
1128 } 1132 }
1141 return dates; 1145 return dates;
1142 } 1146 }
1143 final DateList secondlyDates = getDateListInstance(dates); 1147 final DateList secondlyDates = getDateListInstance(dates);
1144 for (final Iterator i = dates.iterator(); i.hasNext();) { 1148 for (final Iterator i = dates.iterator(); i.hasNext();) {
1145 final Date date = (Date) i.next(); 1149 final Date date = (Date) i.next();
1146 final Calendar cal = Dates.getCalendarInstance(date); 1150 final Calendar cal = getCalendarInstance(date, true);
1147 cal.setTime(date);
1148 for (final Iterator j = getSecondList().iterator(); j.hasNext();) { 1151 for (final Iterator j = getSecondList().iterator(); j.hasNext();) {
1149 final Integer second = (Integer) j.next(); 1152 final Integer second = (Integer) j.next();
1150 cal.set(Calendar.SECOND, second.intValue()); 1153 cal.set(Calendar.SECOND, second.intValue());
1151 secondlyDates.add(Dates.getInstance(cal.getTime(), secondlyDates.getType())); 1154 secondlyDates.add(Dates.getInstance(cal.getTime(), secondlyDates.getType()));
1152 } 1155 }
1216 this.until = until; 1219 this.until = until;
1217 this.count = -1; 1220 this.count = -1;
1218 } 1221 }
1219 1222
1220 /** 1223 /**
1224 * Construct a Calendar object and sets the time.
1225 * @param date
1226 * @param lenient
1227 * @return
1228 */
1229 private Calendar getCalendarInstance(final Date date, final boolean lenient) {
1230 Calendar cal = Dates.getCalendarInstance(date);
1231 // A week should have at least 4 days to be considered as such per RFC5545
1232 cal.setMinimalDaysInFirstWeek(4);
1233 cal.setFirstDayOfWeek(calendarWeekStartDay);
1234 cal.setLenient(lenient);
1235 cal.setTime(date);
1236
1237 return cal;
1238 }
1239
1240 /**
1221 * @param stream 1241 * @param stream
1222 * @throws IOException 1242 * @throws IOException
1223 * @throws ClassNotFoundException 1243 * @throws ClassNotFoundException
1224 */ 1244 */
1225 private void readObject(final java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { 1245 private void readObject(final java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
1231 * Instantiate a new datelist with the same type, timezone and utc settings 1251 * Instantiate a new datelist with the same type, timezone and utc settings
1232 * as the origList. 1252 * as the origList.
1233 * @param origList 1253 * @param origList
1234 * @return a new empty list. 1254 * @return a new empty list.
1235 */ 1255 */
1236 private static final DateList getDateListInstance(final DateList origList) { 1256 private static DateList getDateListInstance(final DateList origList) {
1237 final DateList list = new DateList(origList.getType()); 1257 final DateList list = new DateList(origList.getType());
1238 if (origList.isUtc()) { 1258 if (origList.isUtc()) {
1239 list.setUtc(true); 1259 list.setUtc(true);
1240 } else { 1260 } else {
1241 list.setTimeZone(origList.getTimeZone()); 1261 list.setTimeZone(origList.getTimeZone());

mercurial