Skip to content

Commit 208afa4

Browse files
authored
Merge 4a2cdf0 into be675f3
2 parents be675f3 + 4a2cdf0 commit 208afa4

File tree

3 files changed

+226
-59
lines changed

3 files changed

+226
-59
lines changed

main/src/main/java/org/mobilitydata/gtfsvalidator/validator/BookingRulesEntityValidator.java

+168-57
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5+
import java.util.function.Function;
56
import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice;
67
import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice.FileRefs;
78
import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator;
@@ -17,90 +18,119 @@ public class BookingRulesEntityValidator extends SingleEntityValidator<GtfsBooki
1718

1819
@Override
1920
public void validate(GtfsBookingRules entity, NoticeContainer noticeContainer) {
20-
validateForbiddenRealTimeFields(entity, noticeContainer);
21-
validateSameDayFields(entity, noticeContainer);
21+
validateBookingType(
22+
entity,
23+
GtfsBookingType.REALTIME,
24+
BookingRulesEntityValidator::findForbiddenRealTimeFields,
25+
ForbiddenRealTimeBookingFieldValueNotice::new,
26+
noticeContainer);
27+
validateBookingType(
28+
entity,
29+
GtfsBookingType.SAMEDAY,
30+
BookingRulesEntityValidator::findForbiddenSameDayFields,
31+
ForbiddenSameDayBookingFieldValueNotice::new,
32+
noticeContainer);
33+
validateBookingType(
34+
entity,
35+
GtfsBookingType.PRIORDAY,
36+
BookingRulesEntityValidator::findForbiddenPriorDayFields,
37+
ForbiddenPriorDayBookingFieldValueNotice::new,
38+
noticeContainer);
39+
validatePriorNoticeDurationMin(entity, noticeContainer);
40+
validatePriorNoticeStartDay(entity, noticeContainer);
2241
}
2342

24-
private static void validateForbiddenRealTimeFields(
43+
private static void validatePriorNoticeDurationMin(
2544
GtfsBookingRules entity, NoticeContainer noticeContainer) {
26-
// Only validate entities with REALTIME booking type
27-
if (entity.bookingType() != GtfsBookingType.REALTIME) {
28-
return;
45+
if (entity.hasPriorNoticeDurationMin() && entity.hasPriorNoticeDurationMax()) {
46+
if (entity.priorNoticeDurationMax() < entity.priorNoticeDurationMin()) {
47+
noticeContainer.addValidationNotice(
48+
new InvalidPriorNoticeDurationMinNotice(
49+
entity, entity.priorNoticeDurationMin(), entity.priorNoticeDurationMax()));
50+
}
2951
}
52+
}
3053

31-
// Retrieve the list of forbidden fields
32-
List<String> forbiddenFields = findForbiddenRealTimeFields(entity);
33-
34-
// If there are any forbidden fields, add a validation notice
35-
if (!forbiddenFields.isEmpty()) {
54+
private static void validatePriorNoticeStartDay(
55+
GtfsBookingRules entity, NoticeContainer noticeContainer) {
56+
if (entity.hasPriorNoticeDurationMax() && entity.hasPriorNoticeStartDay()) {
3657
noticeContainer.addValidationNotice(
37-
new ForbiddenRealTimeBookingFieldValueNotice(entity, forbiddenFields));
58+
new ForbiddenPriorNoticeStartDayNotice(
59+
entity, entity.priorNoticeStartDay(), entity.priorNoticeDurationMax()));
3860
}
3961
}
4062

41-
private static void validateSameDayFields(
42-
GtfsBookingRules entity, NoticeContainer noticeContainer) {
43-
// Only validate entities with SAME_DAY booking type
44-
if (entity.bookingType() != GtfsBookingType.SAMEDAY) {
63+
private static void validateBookingType(
64+
GtfsBookingRules entity,
65+
GtfsBookingType bookingType,
66+
Function<GtfsBookingRules, List<String>> forbiddenFieldsFinder,
67+
ValidationNoticeConstructor validationNoticeConstructor,
68+
NoticeContainer noticeContainer) {
69+
70+
if (entity.bookingType() != bookingType) {
4571
return;
4672
}
4773

48-
// Retrieve the list of forbidden fields
49-
List<String> forbiddenFields = findForbiddenSameDayFields(entity);
74+
List<String> forbiddenFields = forbiddenFieldsFinder.apply(entity);
5075

51-
// If there are any forbidden fields, add a validation notice
5276
if (!forbiddenFields.isEmpty()) {
5377
noticeContainer.addValidationNotice(
54-
new ForbiddenSameDayBookingFieldValueNotice(entity, forbiddenFields));
78+
validationNoticeConstructor.create(entity, forbiddenFields));
5579
}
5680
}
5781

5882
private static List<String> findForbiddenSameDayFields(GtfsBookingRules bookingRule) {
59-
List<String> fields = new ArrayList<>();
83+
return findForbiddenFields(
84+
bookingRule.hasPriorNoticeLastDay(),
85+
GtfsBookingRules.PRIOR_NOTICE_LAST_DAY_FIELD_NAME,
86+
bookingRule.hasPriorNoticeLastTime(),
87+
GtfsBookingRules.PRIOR_NOTICE_LAST_TIME_FIELD_NAME,
88+
bookingRule.hasPriorNoticeServiceId(),
89+
GtfsBookingRules.PRIOR_NOTICE_SERVICE_ID_FIELD_NAME);
90+
}
6091

61-
// Check each forbidden field and add its name to the list if it's present
62-
if (bookingRule.hasPriorNoticeLastDay()) {
63-
fields.add(GtfsBookingRules.PRIOR_NOTICE_LAST_DAY_FIELD_NAME);
64-
}
65-
if (bookingRule.hasPriorNoticeLastTime()) {
66-
fields.add(GtfsBookingRules.PRIOR_NOTICE_LAST_TIME_FIELD_NAME);
67-
}
68-
if (bookingRule.hasPriorNoticeServiceId()) {
69-
fields.add(GtfsBookingRules.PRIOR_NOTICE_SERVICE_ID_FIELD_NAME);
70-
}
71-
return fields;
92+
private static List<String> findForbiddenPriorDayFields(GtfsBookingRules bookingRule) {
93+
return findForbiddenFields(
94+
bookingRule.hasPriorNoticeDurationMin(),
95+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MIN_FIELD_NAME,
96+
bookingRule.hasPriorNoticeDurationMax(),
97+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MAX_FIELD_NAME);
7298
}
7399

74-
/** Finds forbidden fields that should not be present for real-time booking rules. */
75-
public static List<String> findForbiddenRealTimeFields(GtfsBookingRules bookingRule) {
76-
List<String> fields = new ArrayList<>();
100+
private static List<String> findForbiddenRealTimeFields(GtfsBookingRules bookingRule) {
101+
return findForbiddenFields(
102+
bookingRule.hasPriorNoticeDurationMin(),
103+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MIN_FIELD_NAME,
104+
bookingRule.hasPriorNoticeDurationMax(),
105+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MAX_FIELD_NAME,
106+
bookingRule.hasPriorNoticeLastDay(),
107+
GtfsBookingRules.PRIOR_NOTICE_LAST_DAY_FIELD_NAME,
108+
bookingRule.hasPriorNoticeLastTime(),
109+
GtfsBookingRules.PRIOR_NOTICE_LAST_TIME_FIELD_NAME,
110+
bookingRule.hasPriorNoticeStartDay(),
111+
GtfsBookingRules.PRIOR_NOTICE_START_DAY_FIELD_NAME,
112+
bookingRule.hasPriorNoticeStartTime(),
113+
GtfsBookingRules.PRIOR_NOTICE_START_TIME_FIELD_NAME,
114+
bookingRule.hasPriorNoticeServiceId(),
115+
GtfsBookingRules.PRIOR_NOTICE_SERVICE_ID_FIELD_NAME);
116+
}
77117

78-
// Check each forbidden field and add its name to the list if it's present
79-
if (bookingRule.hasPriorNoticeDurationMin()) {
80-
fields.add(GtfsBookingRules.PRIOR_NOTICE_DURATION_MIN_FIELD_NAME);
81-
}
82-
if (bookingRule.hasPriorNoticeDurationMax()) {
83-
fields.add(GtfsBookingRules.PRIOR_NOTICE_DURATION_MAX_FIELD_NAME);
84-
}
85-
if (bookingRule.hasPriorNoticeLastDay()) {
86-
fields.add(GtfsBookingRules.PRIOR_NOTICE_LAST_DAY_FIELD_NAME);
87-
}
88-
if (bookingRule.hasPriorNoticeLastTime()) {
89-
fields.add(GtfsBookingRules.PRIOR_NOTICE_LAST_TIME_FIELD_NAME);
90-
}
91-
if (bookingRule.hasPriorNoticeStartDay()) {
92-
fields.add(GtfsBookingRules.PRIOR_NOTICE_START_DAY_FIELD_NAME);
93-
}
94-
if (bookingRule.hasPriorNoticeStartTime()) {
95-
fields.add(GtfsBookingRules.PRIOR_NOTICE_START_TIME_FIELD_NAME);
96-
}
97-
if (bookingRule.hasPriorNoticeServiceId()) {
98-
fields.add(GtfsBookingRules.PRIOR_NOTICE_SERVICE_ID_FIELD_NAME);
118+
private static List<String> findForbiddenFields(Object... conditionsAndFields) {
119+
List<String> fields = new ArrayList<>();
120+
for (int i = 0; i < conditionsAndFields.length; i += 2) {
121+
if ((Boolean) conditionsAndFields[i]) {
122+
fields.add((String) conditionsAndFields[i + 1]);
123+
}
99124
}
100-
101125
return fields;
102126
}
103127

128+
// Abstract Notice Creation using Functional Interface
129+
@FunctionalInterface
130+
private interface ValidationNoticeConstructor {
131+
ValidationNotice create(GtfsBookingRules bookingRule, List<String> forbiddenFields);
132+
}
133+
104134
/** A forbidden field value is present for a real-time booking rule in `booking_rules.txt`. */
105135
@GtfsValidationNotice(
106136
severity = SeverityLevel.ERROR,
@@ -144,4 +174,85 @@ static class ForbiddenSameDayBookingFieldValueNotice extends ValidationNotice {
144174
this.fieldNames = String.join(", ", forbiddenFields);
145175
}
146176
}
177+
178+
/** A forbidden field value is present for a prior-day booking rule in `booking_rules.txt` */
179+
@GtfsValidationNotice(
180+
severity = SeverityLevel.ERROR,
181+
files = @FileRefs(GtfsBookingRulesSchema.class))
182+
static class ForbiddenPriorDayBookingFieldValueNotice extends ValidationNotice {
183+
/** The row number of the faulty record. */
184+
private final int csvRowNumber;
185+
186+
/** The `booking_rules.booking_rule_id` of the faulty record. */
187+
private final String bookingRuleId;
188+
189+
/** The names of the forbidden fields comma-separated. */
190+
private final String fieldNames;
191+
192+
ForbiddenPriorDayBookingFieldValueNotice(
193+
GtfsBookingRules bookingRule, List<String> forbiddenFields) {
194+
this.csvRowNumber = bookingRule.csvRowNumber();
195+
this.bookingRuleId = bookingRule.bookingRuleId();
196+
this.fieldNames = String.join(", ", forbiddenFields);
197+
}
198+
}
199+
200+
/**
201+
* An invalid `prior_notice_duration_min` value is present in a booking rule.
202+
*
203+
* <p>The `prior_notice_duration_max` field value needs to be greater or equal to the
204+
* `prior_notice_duration_min` field value.
205+
*/
206+
@GtfsValidationNotice(
207+
severity = SeverityLevel.ERROR,
208+
files = @FileRefs(GtfsBookingRulesSchema.class))
209+
static class InvalidPriorNoticeDurationMinNotice extends ValidationNotice {
210+
/** The row number of the faulty record. */
211+
private final int csvRowNumber;
212+
213+
/** The `booking_rules.booking_rule_id` of the faulty record. */
214+
private final String bookingRuleId;
215+
216+
/** The value of the `prior_notice_duration_min` field. */
217+
private final int priorNoticeDurationMin;
218+
219+
/** The value of the `prior_notice_duration_max` field. */
220+
private final int priorNoticeDurationMax;
221+
222+
InvalidPriorNoticeDurationMinNotice(
223+
GtfsBookingRules bookingRule, int priorNoticeDurationMin, int priorNoticeDurationMax) {
224+
this.csvRowNumber = bookingRule.csvRowNumber();
225+
this.bookingRuleId = bookingRule.bookingRuleId();
226+
this.priorNoticeDurationMin = priorNoticeDurationMin;
227+
this.priorNoticeDurationMax = priorNoticeDurationMax;
228+
}
229+
}
230+
231+
/**
232+
* An invalid `prior_notice_start_day` value is present when `prior_notice_duration_max` is set.
233+
*/
234+
@GtfsValidationNotice(
235+
severity = SeverityLevel.ERROR,
236+
files = @FileRefs(GtfsBookingRulesSchema.class))
237+
static class ForbiddenPriorNoticeStartDayNotice extends ValidationNotice {
238+
/** The row number of the faulty record. */
239+
private final int csvRowNumber;
240+
241+
/** The `booking_rules.booking_rule_id` of the faulty record. */
242+
private final String bookingRuleId;
243+
244+
/** The value of the `prior_notice_duration_min` field. */
245+
private final int priorNoticeStartDay;
246+
247+
/** The value of the `prior_notice_duration_max` field. */
248+
private final int priorNoticeDurationMax;
249+
250+
ForbiddenPriorNoticeStartDayNotice(
251+
GtfsBookingRules bookingRule, int priorNoticeStartDay, int priorNoticeDurationMax) {
252+
this.csvRowNumber = bookingRule.csvRowNumber();
253+
this.bookingRuleId = bookingRule.bookingRuleId();
254+
this.priorNoticeStartDay = priorNoticeStartDay;
255+
this.priorNoticeDurationMax = priorNoticeDurationMax;
256+
}
257+
}
147258
}

main/src/test/java/org/mobilitydata/gtfsvalidator/validator/BookingRulesEntityValidatorTest.java

+54-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public void sameDayBookingWithForbiddenFieldsShouldGenerateNotice() {
9898
.setBookingRuleId("rule-5")
9999
.setBookingType(GtfsBookingType.SAMEDAY)
100100
.setPriorNoticeLastDay(2) // Forbidden field
101-
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(5000)) // Forbidden field
101+
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(5000))
102102
.build();
103103

104104
assertThat(generateNotices(bookingRule))
@@ -118,4 +118,57 @@ public void sameDayBookingWithoutForbiddenFieldsShouldNotGenerateNotice() {
118118

119119
assertThat(generateNotices(bookingRule)).isEmpty();
120120
}
121+
122+
@Test
123+
public void priorDayBookingWithForbiddenFieldsShouldGenerateNotice() {
124+
GtfsBookingRules bookingRule =
125+
new GtfsBookingRules.Builder()
126+
.setCsvRowNumber(1)
127+
.setBookingRuleId("rule-7")
128+
.setBookingType(GtfsBookingType.PRIORDAY)
129+
.setPriorNoticeDurationMin(30) // Forbidden field
130+
.setPriorNoticeDurationMax(60) // Forbidden field
131+
.build();
132+
133+
assertThat(generateNotices(bookingRule))
134+
.containsExactly(
135+
new BookingRulesEntityValidator.ForbiddenPriorDayBookingFieldValueNotice(
136+
bookingRule,
137+
List.of(
138+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MIN_FIELD_NAME,
139+
GtfsBookingRules.PRIOR_NOTICE_DURATION_MAX_FIELD_NAME)));
140+
}
141+
142+
@Test
143+
public void invalidPriorNoticeDurationMinShouldGenerateNotice() {
144+
GtfsBookingRules bookingRule =
145+
new GtfsBookingRules.Builder()
146+
.setCsvRowNumber(1)
147+
.setBookingRuleId("rule-8")
148+
.setBookingType(GtfsBookingType.SAMEDAY)
149+
.setPriorNoticeDurationMin(60) // Invalid: greater than max
150+
.setPriorNoticeDurationMax(30)
151+
.build();
152+
153+
assertThat(generateNotices(bookingRule))
154+
.containsExactly(
155+
new BookingRulesEntityValidator.InvalidPriorNoticeDurationMinNotice(
156+
bookingRule, 60, 30));
157+
}
158+
159+
@Test
160+
public void forbiddenPriorNoticeStartDayShouldGenerateNotice() {
161+
GtfsBookingRules bookingRule =
162+
new GtfsBookingRules.Builder()
163+
.setCsvRowNumber(1)
164+
.setBookingRuleId("rule-9")
165+
.setBookingType(GtfsBookingType.SAMEDAY)
166+
.setPriorNoticeDurationMax(30) // Duration max is set
167+
.setPriorNoticeStartDay(5) // Forbidden when duration max is set
168+
.build();
169+
170+
assertThat(generateNotices(bookingRule))
171+
.containsExactly(
172+
new BookingRulesEntityValidator.ForbiddenPriorNoticeStartDayNotice(bookingRule, 5, 30));
173+
}
121174
}

main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeFieldsTest.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,10 @@ public void testNoticeClassFieldNames() {
201201
"locationGroupId",
202202
"locationId",
203203
"bookingRuleId",
204-
"fieldNames");
204+
"fieldNames",
205+
"priorNoticeDurationMin",
206+
"priorNoticeDurationMax",
207+
"priorNoticeStartDay");
205208
}
206209

207210
private static List<String> discoverValidationNoticeFieldNames() {

0 commit comments

Comments
 (0)