Skip to content

Commit

Permalink
Implemented more complete limit/expansion rules according to RFC5545
Browse files Browse the repository at this point in the history
  • Loading branch information
benfortuna committed Dec 14, 2018
1 parent b22d38c commit b258396
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 65 deletions.
6 changes: 3 additions & 3 deletions src/main/java/net/fortuna/ical4j/model/Recur.java
Expand Up @@ -335,12 +335,12 @@ private void initTransformers() {
hourList = new NumberList(0, 23, false);
}
if (monthDayList != null) {
transformers.put(BYMONTHDAY, new ByMonthDayRule(monthDayList, Optional.ofNullable(weekStartDay)));
transformers.put(BYMONTHDAY, new ByMonthDayRule(monthDayList, frequency, Optional.ofNullable(weekStartDay)));
} else {
monthDayList = new NumberList(1, 31, true);
}
if (yearDayList != null) {
transformers.put(BYYEARDAY, new ByYearDayRule(yearDayList, Optional.ofNullable(weekStartDay)));
transformers.put(BYYEARDAY, new ByYearDayRule(yearDayList, frequency, Optional.ofNullable(weekStartDay)));
} else {
yearDayList = new NumberList(1, 366, true);
}
Expand Down Expand Up @@ -953,7 +953,7 @@ private DateList getCandidates(final Calendar rootSeed, final Date date, final V

NumberList implicitMonthDayList = new NumberList();
implicitMonthDayList.add(rootSeed.get(Calendar.DAY_OF_MONTH));
ByMonthDayRule implicitRule = new ByMonthDayRule(implicitMonthDayList, Optional.ofNullable(weekStartDay));
ByMonthDayRule implicitRule = new ByMonthDayRule(implicitMonthDayList, frequency, Optional.ofNullable(weekStartDay));
dates = implicitRule.transform(dates);
}

Expand Down
Expand Up @@ -44,27 +44,24 @@ public DateList transform(DateList dates) {
if (EnumSet.of(DAILY, WEEKLY, MONTHLY, YEARLY).contains(frequency)) {
hourlyDates.addAll(new ExpansionFilter(hourlyDates.getType()).apply(date));
} else {
hourlyDates.addAll(new LimitFilter(hourlyDates.getType()).apply(date));
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
hourlyDates.add(limit.get());
}
}
}
return hourlyDates;
}

private class LimitFilter implements Function<Date, List<Date>> {

private final Value type;

public LimitFilter(Value type) {
this.type = type;
}
private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public List<Date> apply(Date date) {
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
if (hourList.contains(cal.get(Calendar.HOUR_OF_DAY))) {
return Arrays.asList(date);
return Optional.of(date);
}
return Collections.emptyList();
return Optional.empty();
}
}

Expand Down
Expand Up @@ -44,27 +44,24 @@ public DateList transform(DateList dates) {
if (EnumSet.of(HOURLY, DAILY, WEEKLY, MONTHLY, YEARLY).contains(frequency)) {
minutelyDates.addAll(new ExpansionFilter(minutelyDates.getType()).apply(date));
} else {
minutelyDates.addAll(new LimitFilter(minutelyDates.getType()).apply(date));
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
minutelyDates.add(limit.get());
}
}
}
return minutelyDates;
}

private class LimitFilter implements Function<Date, List<Date>> {

private final Value type;

public LimitFilter(Value type) {
this.type = type;
}
private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public List<Date> apply(Date date) {
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
if (minuteList.contains(cal.get(Calendar.MINUTE))) {
return Arrays.asList(date);
return Optional.of(date);
}
return Collections.emptyList();
return Optional.empty();
}
}

Expand Down
@@ -1,13 +1,20 @@
package net.fortuna.ical4j.transform.recurrence;

import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.Recur.Frequency;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.util.Dates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Calendar;
import java.util.Optional;
import java.util.*;
import java.util.function.Function;

import static net.fortuna.ical4j.model.Recur.Frequency.MONTHLY;
import static net.fortuna.ical4j.model.Recur.Frequency.YEARLY;

/**
* Applies BYMONTHDAY rules specified in this Recur instance to the specified date list. If no BYMONTHDAY rules are
Expand All @@ -19,13 +26,17 @@ public class ByMonthDayRule extends AbstractDateExpansionRule {

private final NumberList monthDayList;

public ByMonthDayRule(NumberList monthDayList) {
private final Frequency frequency;

public ByMonthDayRule(NumberList monthDayList, Frequency frequency) {
this.monthDayList = monthDayList;
this.frequency = frequency;
}

public ByMonthDayRule(NumberList monthDayList, Optional<WeekDay.Day> weekStartDay) {
public ByMonthDayRule(NumberList monthDayList, Frequency frequency, Optional<WeekDay.Day> weekStartDay) {
super(weekStartDay);
this.monthDayList = monthDayList;
this.frequency = frequency;
}

@Override
Expand All @@ -35,7 +46,43 @@ public DateList transform(DateList dates) {
}
final DateList monthDayDates = Dates.getDateListInstance(dates);
for (final Date date : dates) {
if (EnumSet.of(MONTHLY, YEARLY).contains(frequency)) {
monthDayDates.addAll(new ExpansionFilter(monthDayDates.getType()).apply(date));
} else {
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
monthDayDates.add(limit.get());
}
}
}
return monthDayDates;
}

private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
if (monthDayList.contains(cal.get(Calendar.DAY_OF_MONTH))) {
return Optional.of(date);
}
return Optional.empty();
}
}

private class ExpansionFilter implements Function<Date, List<Date>> {

private final Value type;

public ExpansionFilter(Value type) {
this.type = type;
}

@Override
public List<Date> apply(Date date) {
List<Date> retVal = new ArrayList<>();
final Calendar cal = getCalendarInstance(date, false);
// construct a list of possible month days..
for (final int monthDay : monthDayList) {
if (monthDay == 0 || monthDay < -Dates.MAX_DAYS_PER_MONTH || monthDay > Dates.MAX_DAYS_PER_MONTH) {
if (log.isTraceEnabled()) {
Expand All @@ -56,10 +103,10 @@ public DateList transform(DateList dates) {
cal.set(Calendar.DAY_OF_MONTH, numDaysInMonth);
cal.add(Calendar.DAY_OF_MONTH, monthDay + 1);
}
monthDayDates.add(Dates.getInstance(cal.getTime(), monthDayDates.getType()));
}
retVal.add(Dates.getInstance(cal.getTime(), type));
};
return retVal;
}
return monthDayDates;
}

/**
Expand Down
Expand Up @@ -8,7 +8,10 @@
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.util.Dates;

import java.util.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

/**
Expand Down Expand Up @@ -41,28 +44,25 @@ public DateList transform(DateList dates) {
if (frequency == Frequency.YEARLY) {
monthlyDates.addAll(new ExpansionFilter(monthlyDates.getType()).apply(date));
} else {
monthlyDates.addAll(new LimitFilter(monthlyDates.getType()).apply(date));
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
monthlyDates.add(limit.get());
}
}
}
return monthlyDates;
}

private class LimitFilter implements Function<Date, List<Date>> {

private final Value type;

public LimitFilter(Value type) {
this.type = type;
}
private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public List<Date> apply(Date date) {
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
// Java months are zero-based..
if (monthList.contains(cal.get(Calendar.MONTH) + 1)) {
return Arrays.asList(date);
return Optional.of(date);
}
return Collections.emptyList();
return Optional.empty();
}
}

Expand Down
Expand Up @@ -8,7 +8,10 @@
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.util.Dates;

import java.util.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

/**
Expand Down Expand Up @@ -40,29 +43,26 @@ public DateList transform(DateList dates) {
final DateList secondlyDates = Dates.getDateListInstance(dates);
for (final Date date : dates) {
if (frequency == Frequency.SECONDLY) {
secondlyDates.addAll(new LimitFilter(secondlyDates.getType()).apply(date));
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
secondlyDates.add(limit.get());
}
} else {
secondlyDates.addAll(new ExpansionFilter(secondlyDates.getType()).apply(date));
}
}
return secondlyDates;
}

private class LimitFilter implements Function<Date, List<Date>> {

private final Value type;

public LimitFilter(Value type) {
this.type = type;
}
private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public List<Date> apply(Date date) {
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
if (secondList.contains(cal.get(Calendar.SECOND))) {
return Arrays.asList(date);
return Optional.of(date);
}
return Collections.emptyList();
return Optional.empty();
}
}

Expand Down
@@ -1,13 +1,20 @@
package net.fortuna.ical4j.transform.recurrence;

import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.Recur.Frequency;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.util.Dates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import static net.fortuna.ical4j.model.Recur.Frequency.YEARLY;

/**
* Applies BYYEARDAY rules specified in this Recur instance to the specified date list. If no BYYEARDAY rules are
Expand All @@ -19,13 +26,17 @@ public class ByYearDayRule extends AbstractDateExpansionRule {

private final NumberList yearDayList;

public ByYearDayRule(NumberList yearDayList) {
private final Frequency frequency;

public ByYearDayRule(NumberList yearDayList, Frequency frequency) {
this.yearDayList = yearDayList;
this.frequency = frequency;
}

public ByYearDayRule(NumberList yearDayList, Optional<WeekDay.Day> weekStartDay) {
public ByYearDayRule(NumberList yearDayList, Frequency frequency, Optional<WeekDay.Day> weekStartDay) {
super(weekStartDay);
this.yearDayList = yearDayList;
this.frequency = frequency;
}

@Override
Expand All @@ -35,7 +46,43 @@ public DateList transform(DateList dates) {
}
final DateList yearDayDates = Dates.getDateListInstance(dates);
for (final Date date : dates) {
if (frequency == YEARLY) {
yearDayDates.addAll(new ExpansionFilter(yearDayDates.getType()).apply(date));
} else {
Optional<Date> limit = new LimitFilter().apply(date);
if (limit.isPresent()) {
yearDayDates.add(limit.get());
}
}
}
return yearDayDates;
}

private class LimitFilter implements Function<Date, Optional<Date>> {

@Override
public Optional<Date> apply(Date date) {
final Calendar cal = getCalendarInstance(date, true);
if (yearDayList.contains(cal.get(Calendar.DAY_OF_YEAR))) {
return Optional.of(date);
}
return Optional.empty();
}
}

private class ExpansionFilter implements Function<Date, List<Date>> {

private final Value type;

public ExpansionFilter(Value type) {
this.type = type;
}

@Override
public List<Date> apply(Date date) {
List<Date> retVal = new ArrayList<>();
final Calendar cal = getCalendarInstance(date, false);
// construct a list of possible year days..
for (final int yearDay : yearDayList) {
if (yearDay == 0 || yearDay < -Dates.MAX_DAYS_PER_YEAR || yearDay > Dates.MAX_DAYS_PER_YEAR) {
if (log.isTraceEnabled()) {
Expand All @@ -56,10 +103,10 @@ public DateList transform(DateList dates) {
cal.set(Calendar.DAY_OF_YEAR, numDaysInYear);
cal.add(Calendar.DAY_OF_YEAR, yearDay + 1);
}
yearDayDates.add(Dates.getInstance(cal.getTime(), yearDayDates.getType()));
retVal.add(Dates.getInstance(cal.getTime(), type));
}
return retVal;
}
return yearDayDates;
}

/**
Expand Down

0 comments on commit b258396

Please sign in to comment.