diff --git a/pom.xml b/pom.xml
index e37ec485..a9e9780d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
org.entur.ror
superpom
- 3.2
+ 3.3
no.entur.antu
@@ -26,13 +26,13 @@
17
false
4.4.4
- 4.7
- 9.0.0
- 3.1.34
+ 4.8
+ 9.1.1
+ 3.1.36
1.20.0
2.11.0
1.17
- 3.40.2
+ 3.42.0
5.6.2
0.9.1
2.1.0
diff --git a/src/main/java/no/entur/antu/config/NetexDataConfig.java b/src/main/java/no/entur/antu/config/NetexDataConfig.java
index 48ccb3c9..51adf847 100644
--- a/src/main/java/no/entur/antu/config/NetexDataConfig.java
+++ b/src/main/java/no/entur/antu/config/NetexDataConfig.java
@@ -1,7 +1,10 @@
package no.entur.antu.config;
+import static no.entur.antu.config.cache.CacheConfig.ACTIVE_DATES_CACHE;
import static no.entur.antu.config.cache.CacheConfig.LINE_INFO_CACHE;
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_DAY_TYPES_CACHE;
import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_INTERCHANGE_INFO_CACHE;
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_OPERATING_DAYS_CACHE;
import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_STOPS_CACHE;
import java.util.List;
@@ -25,6 +28,15 @@ NetexDataRepository netexDataRepository(
@Qualifier(
SERVICE_JOURNEY_STOPS_CACHE
) Map>> serviceJourneyStopsCache,
+ @Qualifier(
+ SERVICE_JOURNEY_DAY_TYPES_CACHE
+ ) Map> serviceJourneyDayTypesCache,
+ @Qualifier(
+ ACTIVE_DATES_CACHE
+ ) Map> activeDatesCache,
+ @Qualifier(
+ SERVICE_JOURNEY_OPERATING_DAYS_CACHE
+ ) Map> serviceJourneyOperatingDaysCache,
@Qualifier(
SERVICE_JOURNEY_INTERCHANGE_INFO_CACHE
) Map> serviceJourneyInterchangeInfoCache
@@ -33,6 +45,9 @@ NetexDataRepository netexDataRepository(
redissonClient,
lineInfoCache,
serviceJourneyStopsCache,
+ serviceJourneyDayTypesCache,
+ activeDatesCache,
+ serviceJourneyOperatingDaysCache,
serviceJourneyInterchangeInfoCache
);
}
diff --git a/src/main/java/no/entur/antu/config/TimetableDataValidatorConfig.java b/src/main/java/no/entur/antu/config/TimetableDataValidatorConfig.java
index 8ff9f374..3ee40014 100644
--- a/src/main/java/no/entur/antu/config/TimetableDataValidatorConfig.java
+++ b/src/main/java/no/entur/antu/config/TimetableDataValidatorConfig.java
@@ -18,15 +18,19 @@
import java.util.List;
import java.util.Set;
+import no.entur.antu.netexdata.collectors.DatedServiceJourneysCollector;
import no.entur.antu.netexdata.collectors.LineInfoCollector;
+import no.entur.antu.netexdata.collectors.ServiceJourneyDayTypesCollector;
import no.entur.antu.netexdata.collectors.ServiceJourneyInterchangeInfoCollector;
import no.entur.antu.netexdata.collectors.ServiceJourneyStopsCollector;
+import no.entur.antu.netexdata.collectors.activedatecollector.ActiveDatesCollector;
import no.entur.antu.organisation.OrganisationRepository;
import no.entur.antu.validation.validator.id.NetexIdValidator;
import no.entur.antu.validation.validator.interchange.distance.UnexpectedInterchangeDistanceValidator;
import no.entur.antu.validation.validator.interchange.duplicate.DuplicateInterchangesValidator;
import no.entur.antu.validation.validator.interchange.mandatoryfields.MandatoryFieldsValidator;
import no.entur.antu.validation.validator.interchange.stoppoints.StopPointsInVehicleJourneyValidator;
+import no.entur.antu.validation.validator.interchange.waittime.UnexpectedWaitTimeAndActiveDatesValidator;
import no.entur.antu.validation.validator.journeypattern.stoppoint.distance.UnexpectedDistanceBetweenStopPointsValidator;
import no.entur.antu.validation.validator.journeypattern.stoppoint.identicalstoppoints.IdenticalStopPointsValidator;
import no.entur.antu.validation.validator.journeypattern.stoppoint.samequayref.SameQuayRefValidator;
@@ -42,7 +46,10 @@
import no.entur.antu.validation.validator.servicelink.distance.UnexpectedDistanceInServiceLinkValidator;
import no.entur.antu.validation.validator.servicelink.stoppoints.MismatchedStopPointsValidator;
import no.entur.antu.validation.validator.xpath.EnturTimetableDataValidationTreeFactory;
-import org.entur.netex.validation.validator.*;
+import org.entur.netex.validation.validator.DatasetValidator;
+import org.entur.netex.validation.validator.NetexValidatorsRunner;
+import org.entur.netex.validation.validator.ValidationReportEntryFactory;
+import org.entur.netex.validation.validator.XPathValidator;
import org.entur.netex.validation.validator.id.NetexIdUniquenessValidator;
import org.entur.netex.validation.validator.id.NetexReferenceValidator;
import org.entur.netex.validation.validator.id.ReferenceToValidEntityTypeValidator;
@@ -106,6 +113,19 @@ public DuplicateLineNameValidator duplicateLineNameValidator(
);
}
+ @Bean
+ public UnexpectedWaitTimeAndActiveDatesValidator unexpectedWaitTimeValidator(
+ @Qualifier(
+ "validationReportEntryFactory"
+ ) ValidationReportEntryFactory validationReportEntryFactory,
+ NetexDataRepository netexDataRepository
+ ) {
+ return new UnexpectedWaitTimeAndActiveDatesValidator(
+ validationReportEntryFactory,
+ netexDataRepository
+ );
+ }
+
@Bean
public NetexValidatorsRunner timetableDataValidatorsRunner(
@Qualifier(
@@ -125,10 +145,14 @@ public NetexValidatorsRunner timetableDataValidatorsRunner(
) NetexIdUniquenessValidator netexIdUniquenessValidator,
StopPointsInVehicleJourneyValidator stopPointsInVehicleJourneyValidator,
DuplicateLineNameValidator duplicateLineNameValidator,
+ UnexpectedWaitTimeAndActiveDatesValidator unexpectedWaitTimeAndActiveDatesValidator,
LineInfoCollector lineInfoCollector,
ServiceJourneyStopsCollector serviceJourneyStopsCollector,
ServiceJourneyInterchangeInfoCollector serviceJourneyInterchangeInfoCollector,
- CommonDataRepositoryLoader commonDataRepository,
+ ActiveDatesCollector activeDatesCollector,
+ ServiceJourneyDayTypesCollector serviceJourneyDayTypesCollector,
+ DatedServiceJourneysCollector datedServiceJourneysCollector,
+ CommonDataRepositoryLoader commonDataRepositoryLoader,
NetexDataRepository netexDataRepository,
StopPlaceRepository stopPlaceRepository
) {
@@ -165,13 +189,17 @@ public NetexValidatorsRunner timetableDataValidatorsRunner(
List netexTimetableDatasetValidators = List.of(
duplicateLineNameValidator,
- stopPointsInVehicleJourneyValidator
+ stopPointsInVehicleJourneyValidator,
+ unexpectedWaitTimeAndActiveDatesValidator
);
List commonDataCollectors = List.of(
lineInfoCollector,
serviceJourneyInterchangeInfoCollector,
- serviceJourneyStopsCollector
+ serviceJourneyStopsCollector,
+ activeDatesCollector,
+ serviceJourneyDayTypesCollector,
+ datedServiceJourneysCollector
);
return NetexValidatorsRunner
@@ -182,7 +210,7 @@ public NetexValidatorsRunner timetableDataValidatorsRunner(
.withJaxbValidators(jaxbValidators)
.withDatasetValidators(netexTimetableDatasetValidators)
.withNetexDataCollectors(commonDataCollectors)
- .withCommonDataRepository(commonDataRepository)
+ .withCommonDataRepository(commonDataRepositoryLoader)
.withNetexDataRepository(netexDataRepository)
.withStopPlaceRepository(stopPlaceRepository)
.withValidationReportEntryFactory(validationReportEntryFactory)
diff --git a/src/main/java/no/entur/antu/config/cache/CacheConfig.java b/src/main/java/no/entur/antu/config/cache/CacheConfig.java
index 7f88d9ef..0eab98a4 100644
--- a/src/main/java/no/entur/antu/config/cache/CacheConfig.java
+++ b/src/main/java/no/entur/antu/config/cache/CacheConfig.java
@@ -40,8 +40,13 @@ public class CacheConfig {
public static final String LINE_INFO_CACHE = "linesInfoCache";
public static final String SERVICE_JOURNEY_INTERCHANGE_INFO_CACHE =
"serviceJourneyInterchangeInfoCache";
+ public static final String SERVICE_JOURNEY_DAY_TYPES_CACHE =
+ "serviceJourneyDayTypesCache";
public static final String SERVICE_JOURNEY_STOPS_CACHE =
"serviceJourneyStopsCache";
+ public static final String SERVICE_JOURNEY_OPERATING_DAYS_CACHE =
+ "serviceJourneyOperatingDaysCache";
+ public static final String ACTIVE_DATES_CACHE = "activeDatesCache";
public static final String QUAY_ID_NOT_FOUND_CACHE = "quayIdNotFoundCache";
private static final Kryo5Codec DEFAULT_CODEC = new Kryo5Codec();
@@ -172,6 +177,17 @@ public Map> serviceJourneyInterchangeInfoCache(
);
}
+ @Bean(name = SERVICE_JOURNEY_DAY_TYPES_CACHE)
+ public Map> serviceJourneyDayTypesCache(
+ RedissonClient redissonClient
+ ) {
+ return getOrCreateReportScopedCache(
+ redissonClient,
+ SERVICE_JOURNEY_DAY_TYPES_CACHE,
+ new CompositeCodec(new StringCodec(), new StringCodec())
+ );
+ }
+
@Bean(name = SERVICE_JOURNEY_STOPS_CACHE)
public Map>> serviceJourneyStopsCache(
RedissonClient redissonClient
@@ -183,6 +199,28 @@ public Map>> serviceJourneyStopsCache(
);
}
+ @Bean(name = ACTIVE_DATES_CACHE)
+ public Map> activeDatesCache(
+ RedissonClient redissonClient
+ ) {
+ return getOrCreateReportScopedCache(
+ redissonClient,
+ ACTIVE_DATES_CACHE,
+ new CompositeCodec(new StringCodec(), new StringCodec())
+ );
+ }
+
+ @Bean(name = SERVICE_JOURNEY_OPERATING_DAYS_CACHE)
+ public Map> serviceJourneyOperatingDaysCache(
+ RedissonClient redissonClient
+ ) {
+ return getOrCreateReportScopedCache(
+ redissonClient,
+ SERVICE_JOURNEY_OPERATING_DAYS_CACHE,
+ new CompositeCodec(new StringCodec(), new StringCodec())
+ );
+ }
+
@Bean
public NetexIdRepository netexIdRepository(
RedissonClient redissonClient,
diff --git a/src/main/java/no/entur/antu/config/cache/NetexDataCollectorConfig.java b/src/main/java/no/entur/antu/config/cache/NetexDataCollectorConfig.java
index 5e8e9316..c8787e86 100644
--- a/src/main/java/no/entur/antu/config/cache/NetexDataCollectorConfig.java
+++ b/src/main/java/no/entur/antu/config/cache/NetexDataCollectorConfig.java
@@ -1,14 +1,20 @@
package no.entur.antu.config.cache;
+import static no.entur.antu.config.cache.CacheConfig.ACTIVE_DATES_CACHE;
import static no.entur.antu.config.cache.CacheConfig.LINE_INFO_CACHE;
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_DAY_TYPES_CACHE;
import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_INTERCHANGE_INFO_CACHE;
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_OPERATING_DAYS_CACHE;
import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_STOPS_CACHE;
import java.util.List;
import java.util.Map;
+import no.entur.antu.netexdata.collectors.DatedServiceJourneysCollector;
import no.entur.antu.netexdata.collectors.LineInfoCollector;
+import no.entur.antu.netexdata.collectors.ServiceJourneyDayTypesCollector;
import no.entur.antu.netexdata.collectors.ServiceJourneyInterchangeInfoCollector;
import no.entur.antu.netexdata.collectors.ServiceJourneyStopsCollector;
+import no.entur.antu.netexdata.collectors.activedatecollector.ActiveDatesCollector;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
@@ -25,6 +31,29 @@ public LineInfoCollector lineInfoScraper(
return new LineInfoCollector(redissonClient, lineInfoCache);
}
+ @Bean
+ public ActiveDatesCollector activeDatesCollector(
+ RedissonClient redissonClient,
+ @Qualifier(
+ ACTIVE_DATES_CACHE
+ ) Map> activeDatesCache
+ ) {
+ return new ActiveDatesCollector(redissonClient, activeDatesCache);
+ }
+
+ @Bean
+ public DatedServiceJourneysCollector datedServiceJourneysCollector(
+ RedissonClient redissonClient,
+ @Qualifier(
+ SERVICE_JOURNEY_OPERATING_DAYS_CACHE
+ ) Map> serviceJourneyOperatingDaysCache
+ ) {
+ return new DatedServiceJourneysCollector(
+ redissonClient,
+ serviceJourneyOperatingDaysCache
+ );
+ }
+
@Bean
public ServiceJourneyInterchangeInfoCollector serviceJourneyInterchangeInfoCollector(
RedissonClient redissonClient,
@@ -38,6 +67,19 @@ public ServiceJourneyInterchangeInfoCollector serviceJourneyInterchangeInfoColle
);
}
+ @Bean
+ public ServiceJourneyDayTypesCollector serviceJourneyDayTypesCollector(
+ RedissonClient redissonClient,
+ @Qualifier(
+ SERVICE_JOURNEY_DAY_TYPES_CACHE
+ ) Map> serviceJourneyDayTypesCache
+ ) {
+ return new ServiceJourneyDayTypesCollector(
+ redissonClient,
+ serviceJourneyDayTypesCache
+ );
+ }
+
@Bean
public ServiceJourneyStopsCollector serviceJourneyStopsCollector(
RedissonClient redissonClient,
diff --git a/src/main/java/no/entur/antu/finland/validator/EnturTimetableDataFinlandValidationTreeFactory.java b/src/main/java/no/entur/antu/finland/validator/EnturTimetableDataFinlandValidationTreeFactory.java
index 11687ce0..82ae612e 100644
--- a/src/main/java/no/entur/antu/finland/validator/EnturTimetableDataFinlandValidationTreeFactory.java
+++ b/src/main/java/no/entur/antu/finland/validator/EnturTimetableDataFinlandValidationTreeFactory.java
@@ -23,6 +23,8 @@ public EnturTimetableDataFinlandValidationTreeFactory() {
@Override
public ValidationTreeBuilder builder() {
+ ValidationTreeBuilder builder = super.builder();
+
// accept SiteFrame, they are part of Finnish datasets
siteFrameValidationTreeBuilder()
.removeRuleForCommonFile(
@@ -44,6 +46,6 @@ public ValidationTreeBuilder builder() {
.removeRule(
DefaultServiceFrameValidationTreeFactory.CODE_PASSENGER_STOP_ASSIGNMENT_3
);
- return super.builder();
+ return builder;
}
}
diff --git a/src/main/java/no/entur/antu/netexdata/DefaultNetexDataRepository.java b/src/main/java/no/entur/antu/netexdata/DefaultNetexDataRepository.java
index e7125cb7..1e260ac1 100644
--- a/src/main/java/no/entur/antu/netexdata/DefaultNetexDataRepository.java
+++ b/src/main/java/no/entur/antu/netexdata/DefaultNetexDataRepository.java
@@ -5,6 +5,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import no.entur.antu.exception.AntuException;
import org.entur.netex.validation.validator.model.ActiveDates;
import org.entur.netex.validation.validator.model.ActiveDatesId;
@@ -23,15 +24,24 @@ public class DefaultNetexDataRepository implements NetexDataRepositoryLoader {
private final Map> lineInfoCache;
private final Map>> serviceJourneyStopsCache;
+ private final Map> serviceJourneyDayTypesCache;
+ private final Map> activeDatesCache;
+ private final Map> serviceJourneyOperatingDaysCache;
private final Map> serviceJourneyInterchangeInfoCache;
public DefaultNetexDataRepository(
Map> lineInfoCache,
Map>> serviceJourneyStopsCache,
+ Map> serviceJourneyDayTypesCache,
+ Map> activeDatesCache,
+ Map> serviceJourneyOperatingDaysCache,
Map> serviceJourneyInterchangeInfoCache
) {
this.lineInfoCache = lineInfoCache;
this.serviceJourneyStopsCache = serviceJourneyStopsCache;
+ this.serviceJourneyDayTypesCache = serviceJourneyDayTypesCache;
+ this.activeDatesCache = activeDatesCache;
+ this.serviceJourneyOperatingDaysCache = serviceJourneyOperatingDaysCache;
this.serviceJourneyInterchangeInfoCache =
serviceJourneyInterchangeInfoCache;
}
@@ -67,40 +77,79 @@ public Map> serviceJourneyStops(
);
}
- @Override
- public List serviceJourneyInterchangeInfos(
+ public Map> serviceJourneyDayTypes(
String validationReportId
) {
- return Optional
- .ofNullable(serviceJourneyInterchangeInfoCache)
- .map(Map::entrySet)
+ return serviceJourneyDayTypesCache
+ .keySet()
.stream()
- .flatMap(Set::stream)
- .filter(entry -> entry.getKey().startsWith(validationReportId))
- .flatMap(entry -> entry.getValue().stream())
- .map(ServiceJourneyInterchangeInfo::fromString)
- .toList();
+ .filter(k -> k.startsWith(validationReportId))
+ .map(serviceJourneyDayTypesCache::get)
+ .flatMap(m -> m.entrySet().stream())
+ .collect(
+ Collectors.toMap(
+ entry -> ServiceJourneyId.ofValidId(entry.getKey()),
+ entry ->
+ Stream.of(entry.getValue().split(",")).map(DayTypeId::new).toList(),
+ (p, n) -> n
+ )
+ );
}
@Override
- public Map> serviceJourneyDayTypes(
+ public Map> serviceJourneyOperatingDays(
String validationReportId
) {
- throw new UnsupportedOperationException();
+ return serviceJourneyOperatingDaysCache
+ .keySet()
+ .stream()
+ .filter(k -> k.startsWith(validationReportId))
+ .map(serviceJourneyOperatingDaysCache::get)
+ .flatMap(m -> m.entrySet().stream())
+ .collect(
+ Collectors.toMap(
+ entry -> ServiceJourneyId.ofValidId(entry.getKey()),
+ entry ->
+ Stream
+ .of(entry.getValue().split(","))
+ .map(OperatingDayId::new)
+ .toList(),
+ (p, n) -> n
+ )
+ );
}
- @Override
public Map activeDates(
String validationReportId
) {
- throw new UnsupportedOperationException();
+ return activeDatesCache
+ .keySet()
+ .stream()
+ .filter(k -> k.startsWith(validationReportId))
+ .map(activeDatesCache::get)
+ .flatMap(m -> m.entrySet().stream())
+ .collect(
+ Collectors.toMap(
+ entry -> ActiveDatesId.of(entry.getKey()),
+ entry -> ActiveDates.fromString(entry.getValue()),
+ (p, n) -> n
+ )
+ );
}
@Override
- public Map> serviceJourneyOperatingDays(
+ public List serviceJourneyInterchangeInfos(
String validationReportId
) {
- throw new UnsupportedOperationException();
+ return Optional
+ .ofNullable(serviceJourneyInterchangeInfoCache)
+ .map(Map::entrySet)
+ .stream()
+ .flatMap(Set::stream)
+ .filter(entry -> entry.getKey().startsWith(validationReportId))
+ .flatMap(entry -> entry.getValue().stream())
+ .map(ServiceJourneyInterchangeInfo::fromString)
+ .toList();
}
@Override
diff --git a/src/main/java/no/entur/antu/netexdata/RedisNetexDataRepository.java b/src/main/java/no/entur/antu/netexdata/RedisNetexDataRepository.java
index 1ec4efc6..89fabdc0 100644
--- a/src/main/java/no/entur/antu/netexdata/RedisNetexDataRepository.java
+++ b/src/main/java/no/entur/antu/netexdata/RedisNetexDataRepository.java
@@ -12,11 +12,17 @@ public RedisNetexDataRepository(
RedissonClient redissonClient,
Map> lineInfoCache,
Map>> serviceJourneyStopsCache,
+ Map> serviceJourneyDayTypesCache,
+ Map> activeDatesCache,
+ Map> serviceJourneyOperatingDaysCache,
Map> serviceJourneyInterchangeInfoCache
) {
super(
lineInfoCache,
serviceJourneyStopsCache,
+ serviceJourneyDayTypesCache,
+ activeDatesCache,
+ serviceJourneyOperatingDaysCache,
serviceJourneyInterchangeInfoCache
);
this.redissonClient = redissonClient;
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollector.java b/src/main/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollector.java
new file mode 100644
index 00000000..d4866fd1
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollector.java
@@ -0,0 +1,150 @@
+package no.entur.antu.netexdata.collectors;
+
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_OPERATING_DAYS_CACHE;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import jakarta.xml.bind.JAXBElement;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.entur.netex.validation.validator.jaxb.NetexDataCollector;
+import org.redisson.api.RLock;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.rutebanken.netex.model.DatedServiceJourney;
+import org.rutebanken.netex.model.ServiceJourneyRefStructure;
+
+public class DatedServiceJourneysCollector extends NetexDataCollector {
+
+ private final RedissonClient redissonClient;
+ private final Map> serviceJourneyOperatingDaysCache;
+
+ public DatedServiceJourneysCollector(
+ RedissonClient redissonClient,
+ Map> serviceJourneyOperatingDaysCache
+ ) {
+ this.redissonClient = redissonClient;
+ this.serviceJourneyOperatingDaysCache = serviceJourneyOperatingDaysCache;
+ }
+
+ @Override
+ protected void collectDataFromLineFile(
+ JAXBValidationContext validationContext
+ ) {
+ Map operatingDaysPerServiceJourney =
+ getOperatingDaysPerServiceJourneyIsAsStrings(validationContext);
+
+ if (!operatingDaysPerServiceJourney.isEmpty()) {
+ addServiceJourneyOperatingDays(
+ validationContext.getValidationReportId(),
+ validationContext.getFileName(),
+ operatingDaysPerServiceJourney
+ );
+ }
+ }
+
+ static Map getOperatingDaysPerServiceJourneyIsAsStrings(
+ JAXBValidationContext validationContext
+ ) {
+ Multimap serviceJourneyOperatingDays = validationContext
+ .datedServiceJourneys()
+ .stream()
+ .map(DatedServiceJourneysCollector::operatingDaysRefsPerServiceJourney)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toMultimap(Map.Entry::getKey, Map.Entry::getValue));
+
+ return serviceJourneyOperatingDays
+ .asMap()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> String.join(",", entry.getValue())
+ )
+ );
+ }
+
+ @Override
+ protected void collectDataFromCommonFile(
+ JAXBValidationContext validationContext
+ ) {
+ // No service journeys and journey patterns in common files
+ }
+
+ /**
+ * List of operating days references per service journey.
+ * There is only one serviceJourneyRef per datedServiceJourney.
+ */
+ private static Optional> operatingDaysRefsPerServiceJourney(
+ DatedServiceJourney datedServiceJourney
+ ) {
+ return datedServiceJourney
+ .getJourneyRef()
+ .stream()
+ .map(JAXBElement::getValue)
+ .filter(ServiceJourneyRefStructure.class::isInstance)
+ .map(ServiceJourneyRefStructure.class::cast)
+ .filter(serviceJourneyRef -> serviceJourneyRef.getRef() != null)
+ .map(serviceJourneyRef ->
+ Map.entry(
+ serviceJourneyRef.getRef(),
+ datedServiceJourney.getOperatingDayRef().getRef()
+ )
+ )
+ .findFirst();
+ }
+
+ private void addServiceJourneyOperatingDays(
+ String validationReportId,
+ String filename,
+ Map serviceJourneyOperatingDays
+ ) {
+ RLock lock = redissonClient.getLock(validationReportId);
+ try {
+ lock.lock();
+
+ String keyName =
+ validationReportId +
+ "_" +
+ SERVICE_JOURNEY_OPERATING_DAYS_CACHE +
+ "_" +
+ filename;
+
+ RMap serviceJourneyOperatingDaysForFile =
+ redissonClient.getMap(keyName);
+ serviceJourneyOperatingDaysForFile.putAll(serviceJourneyOperatingDays);
+ serviceJourneyOperatingDaysCache.put(
+ keyName,
+ serviceJourneyOperatingDaysForFile
+ );
+ } finally {
+ if (lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+ }
+
+ static Collector> toMultimap(
+ Function keyMapper,
+ Function valueMapper
+ ) {
+ return Collector.of(
+ ArrayListMultimap::create, // Supplier: Create a new Multimap
+ (multimap, assignment) ->
+ multimap.put(
+ keyMapper.apply(assignment),
+ valueMapper.apply(assignment)
+ ), // Accumulator
+ (m1, m2) -> { // Combiner
+ m1.putAll(m2);
+ return m1;
+ }
+ );
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/LineInfoCollector.java b/src/main/java/no/entur/antu/netexdata/collectors/LineInfoCollector.java
index e763ea14..9a78e9ac 100644
--- a/src/main/java/no/entur/antu/netexdata/collectors/LineInfoCollector.java
+++ b/src/main/java/no/entur/antu/netexdata/collectors/LineInfoCollector.java
@@ -35,7 +35,7 @@ protected void collectDataFromLineFile(
@Override
protected void collectDataFromCommonFile(
- JAXBValidationContext validationContext
+ JAXBValidationContext jaxbValidationContext
) {
// No Lines in common files
}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollector.java b/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollector.java
new file mode 100644
index 00000000..8fe80316
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollector.java
@@ -0,0 +1,103 @@
+package no.entur.antu.netexdata.collectors;
+
+import static no.entur.antu.config.cache.CacheConfig.SERVICE_JOURNEY_DAY_TYPES_CACHE;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import no.entur.antu.validation.validator.support.NetexUtils;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.entur.netex.validation.validator.jaxb.NetexDataCollector;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.redisson.api.RLock;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+
+public class ServiceJourneyDayTypesCollector extends NetexDataCollector {
+
+ private final RedissonClient redissonClient;
+ private final Map> serviceJourneyDayTypesCache;
+
+ public ServiceJourneyDayTypesCollector(
+ RedissonClient redissonClient,
+ Map> serviceJourneyDayTypesCache
+ ) {
+ this.redissonClient = redissonClient;
+ this.serviceJourneyDayTypesCache = serviceJourneyDayTypesCache;
+ }
+
+ @Override
+ protected void collectDataFromLineFile(
+ JAXBValidationContext validationContext
+ ) {
+ Map serviceJourneyDayTypes =
+ getDayTypesPerServiceJourneyIdAsStrings(validationContext);
+
+ if (!serviceJourneyDayTypes.isEmpty()) {
+ addServiceJourneyDayTypes(
+ validationContext.getValidationReportId(),
+ validationContext.getFileName(),
+ serviceJourneyDayTypes
+ );
+ }
+ }
+
+ static Map getDayTypesPerServiceJourneyIdAsStrings(
+ JAXBValidationContext validationContext
+ ) {
+ return NetexUtils
+ .validServiceJourneys(validationContext)
+ .stream()
+ .map(serviceJourney ->
+ Map.entry(
+ serviceJourney.getId(),
+ DayTypeId
+ .of(serviceJourney)
+ .stream()
+ .map(DayTypeId::toString)
+ .toList()
+ )
+ )
+ .filter(entry -> !entry.getValue().isEmpty())
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> String.join(",", entry.getValue())
+ )
+ );
+ }
+
+ @Override
+ protected void collectDataFromCommonFile(
+ JAXBValidationContext validationContext
+ ) {
+ // No service journeys and journey patterns in common files
+ }
+
+ private void addServiceJourneyDayTypes(
+ String validationReportId,
+ String filename,
+ Map serviceJourneyDayTypes
+ ) {
+ RLock lock = redissonClient.getLock(validationReportId);
+ try {
+ lock.lock();
+
+ String keyName =
+ validationReportId +
+ "_" +
+ SERVICE_JOURNEY_DAY_TYPES_CACHE +
+ "_" +
+ filename;
+
+ RMap serviceJourneyDayTypesMap = redissonClient.getMap(
+ keyName
+ );
+ serviceJourneyDayTypesMap.putAll(serviceJourneyDayTypes);
+ serviceJourneyDayTypesCache.put(keyName, serviceJourneyDayTypesMap);
+ } finally {
+ if (lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyStopsCollector.java b/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyStopsCollector.java
index caa29eb3..03a9222d 100644
--- a/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyStopsCollector.java
+++ b/src/main/java/no/entur/antu/netexdata/collectors/ServiceJourneyStopsCollector.java
@@ -13,9 +13,7 @@
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
-import org.springframework.stereotype.Component;
-@Component
public class ServiceJourneyStopsCollector extends NetexDataCollector {
private final RedissonClient redissonClient;
@@ -38,8 +36,8 @@ protected void collectDataFromLineFile(
// service journeys in them?
// if (validationContext.serviceJourneyInterchanges().findAny().isPresent()) {}
- Map> serviceJourneyStops = validationContext
- .serviceJourneys()
+ Map> serviceJourneyStops = NetexUtils
+ .validServiceJourneys(validationContext)
.stream()
.map(serviceJourney -> {
Map scheduledStopPointIdMap =
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollector.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollector.java
new file mode 100644
index 00000000..08972bf8
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollector.java
@@ -0,0 +1,210 @@
+package no.entur.antu.netexdata.collectors.activedatecollector;
+
+import static no.entur.antu.config.cache.CacheConfig.ACTIVE_DATES_CACHE;
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.getValidityForFrameOrDefault;
+
+import jakarta.xml.bind.JAXBElement;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import no.entur.antu.netexdata.collectors.activedatecollector.calender.ActiveDatesBuilder;
+import no.entur.antu.netexdata.collectors.activedatecollector.calender.ServiceCalendarFrameObject;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.entur.netex.validation.validator.jaxb.NetexDataCollector;
+import org.redisson.api.RLock;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.rutebanken.netex.model.CompositeFrame;
+import org.rutebanken.netex.model.ServiceCalendarFrame;
+import org.rutebanken.netex.model.ValidBetween;
+
+public class ActiveDatesCollector extends NetexDataCollector {
+
+ private final RedissonClient redissonClient;
+ private final Map> activeDatesCache;
+
+ public ActiveDatesCollector(
+ RedissonClient redissonClient,
+ Map> activeDatesCache
+ ) {
+ this.redissonClient = redissonClient;
+ this.activeDatesCache = activeDatesCache;
+ }
+
+ @Override
+ protected void collectDataFromLineFile(
+ JAXBValidationContext validationContext
+ ) {
+ collectData(validationContext);
+ }
+
+ @Override
+ protected void collectDataFromCommonFile(
+ JAXBValidationContext validationContext
+ ) {
+ collectData(validationContext);
+ }
+
+ private void collectData(JAXBValidationContext validationContext) {
+ List serviceCalendarFrameObjects =
+ parseServiceCalendarFrame(validationContext);
+
+ Map activeDates = getActiveDatesPerIdAsMapOfStrings(
+ serviceCalendarFrameObjects
+ );
+
+ if (!activeDates.isEmpty()) {
+ storeActiveDates(
+ validationContext.getValidationReportId(),
+ validationContext.getFileName(),
+ activeDates
+ );
+ }
+ }
+
+ static List parseServiceCalendarFrame(
+ JAXBValidationContext validationContext
+ ) {
+ // TODO: Is it possible that the file has ServiceCalendarFrames has ServiceCalendarFrames outside the compositeFrame,
+ // while compositeFrame also exists? if yes, parse both and combine the result.
+ if (validationContext.hasCompositeFrames()) {
+ return validationContext
+ .compositeFrames()
+ .stream()
+ .map(ActiveDatesCollector::getServiceCalendarFrameObjects)
+ .flatMap(List::stream)
+ .toList();
+ } else if (validationContext.hasServiceCalendarFrames()) {
+ return validationContext
+ .serviceCalendarFrames()
+ .stream()
+ .map(ActiveDatesCollector::getServiceCalendarFrameObjects)
+ .toList();
+ }
+ return List.of();
+ }
+
+ static Map getActiveDatesPerIdAsMapOfStrings(
+ List serviceCalendarFrameObjects
+ ) {
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map activeDatesPerDayTypes = getActiveDatesPerDayTypeId(
+ serviceCalendarFrameObjects,
+ activeDatesBuilder
+ );
+
+ // This is needed for DatedServiceJourneys
+ Map activeDatesPerOperationDays =
+ getActiveDatesPerOperatingDayId(
+ serviceCalendarFrameObjects,
+ activeDatesBuilder
+ );
+
+ return Stream
+ .concat(
+ activeDatesPerDayTypes.entrySet().stream(),
+ activeDatesPerOperationDays.entrySet().stream()
+ )
+ .collect(
+ Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1)
+ );
+ }
+
+ private static Map getActiveDatesPerOperatingDayId(
+ List serviceCalendarFrameObjects,
+ ActiveDatesBuilder activeDatesBuilder
+ ) {
+ return serviceCalendarFrameObjects
+ .stream()
+ .map(activeDatesBuilder::buildPerOperatingDay)
+ .flatMap(map -> map.entrySet().stream())
+ .filter(entry -> entry.getValue().isValid())
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue().toString(),
+ (v1, v2) -> v1
+ )
+ );
+ }
+
+ private static Map getActiveDatesPerDayTypeId(
+ List serviceCalendarFrameObjects,
+ ActiveDatesBuilder activeDatesBuilder
+ ) {
+ return serviceCalendarFrameObjects
+ .stream()
+ .map(activeDatesBuilder::buildPerDayType)
+ .flatMap(map -> map.entrySet().stream())
+ .filter(entry -> entry.getValue().isValid())
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue().toString(),
+ (v1, v2) -> v1
+ )
+ );
+ }
+
+ private void storeActiveDates(
+ String validationReportId,
+ String filename,
+ Map activeDates
+ ) {
+ RLock lock = redissonClient.getLock(validationReportId);
+ try {
+ lock.lock();
+
+ String keyName =
+ validationReportId + "_" + ACTIVE_DATES_CACHE + "_" + filename;
+
+ RMap activeDatesForFile = redissonClient.getMap(keyName);
+ activeDatesForFile.putAll(activeDates);
+ activeDatesCache.put(keyName, activeDatesForFile);
+ } finally {
+ if (lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+ }
+
+ private static List getServiceCalendarFrameObjects(
+ CompositeFrame compositeFrame
+ ) {
+ // When grouping Frames into a CompositeFrame, ValidityCondition must be the same for all its frames.
+ // That is, ValidityCondition is not set per frame, but is implicitly controlled from the CompositeFrame.
+ ValidBetween validityForCompositeFrame = getValidityForFrameOrDefault(
+ compositeFrame,
+ null
+ );
+ return compositeFrame
+ .getFrames()
+ .getCommonFrame()
+ .stream()
+ .map(JAXBElement::getValue)
+ .filter(ServiceCalendarFrame.class::isInstance)
+ .map(ServiceCalendarFrame.class::cast)
+ .map(serviceCalendarFrame ->
+ ServiceCalendarFrameObject.ofNullable(
+ serviceCalendarFrame,
+ validityForCompositeFrame
+ )
+ )
+ .toList();
+ }
+
+ private static ServiceCalendarFrameObject getServiceCalendarFrameObjects(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ ValidBetween validityForServiceCalendarFrame = getValidityForFrameOrDefault(
+ serviceCalendarFrame,
+ null
+ );
+ return ServiceCalendarFrameObject.ofNullable(
+ serviceCalendarFrame,
+ validityForServiceCalendarFrame
+ );
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilder.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilder.java
new file mode 100644
index 00000000..a648e87c
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilder.java
@@ -0,0 +1,389 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.getOrDefault;
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.isWithinValidRange;
+
+import jakarta.xml.bind.JAXBElement;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import org.entur.netex.validation.validator.model.ActiveDates;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.rutebanken.netex.model.DayOfWeekEnumeration;
+import org.rutebanken.netex.model.DayType;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingDay_VersionStructure;
+import org.rutebanken.netex.model.PropertyOfDay;
+import org.rutebanken.netex.model.ValidBetween;
+import org.rutebanken.netex.model.VersionOfObjectRefStructure;
+
+public class ActiveDatesBuilder {
+
+ private final Map activeDatesForDayTypeRef =
+ new HashMap<>();
+ private final Map> excludedDates = new HashMap<>();
+ private final Map daysForDayTypeId = new HashMap<>();
+
+ public Map buildPerDayType(
+ ServiceCalendarFrameObject serviceCalendarFrameObject
+ ) {
+ // Creating the DayTypes for ServiceCalendarFrame
+ serviceCalendarFrameObject
+ .calendarData()
+ .dayTypes()
+ .forEach((dayTypeRef, dayType) -> {
+ activeDatesForDayTypeRef.put(
+ dayTypeRef,
+ new ActiveDates(new ArrayList<>())
+ );
+ addDayType(dayTypeRef, dayType);
+ });
+
+ if (serviceCalendarFrameObject.serviceCalendar() != null) {
+ // Creating the DayTypes for ServiceCalendar
+ serviceCalendarFrameObject
+ .serviceCalendar()
+ .calendarData()
+ .dayTypes()
+ .forEach((dayTypeRef, dayType) -> {
+ activeDatesForDayTypeRef.put(
+ dayTypeRef,
+ new ActiveDates(new ArrayList<>())
+ );
+ addDayType(dayTypeRef, dayType);
+ });
+ }
+
+ // Creating ActiveDates form DayTypeAssignments for Dates in ServiceCalendarFrame
+ activeDatesForDates(
+ serviceCalendarFrameObject.calendarData(),
+ serviceCalendarFrameObject.validBetween()
+ );
+
+ if (serviceCalendarFrameObject.serviceCalendar() != null) {
+ // Creating ActiveDates form DayTypeAssignments for Dates in ServiceCalendar
+ activeDatesForDates(
+ serviceCalendarFrameObject.serviceCalendar().calendarData(),
+ serviceCalendarFrameObject.serviceCalendar().validBetween()
+ );
+ }
+
+ // Creating ActiveDates form DayTypeAssignments for OperatingDays for ServiceCalendarFrame
+ activeDatesForOperatingDays(
+ serviceCalendarFrameObject.calendarData(),
+ serviceCalendarFrameObject.validBetween()
+ );
+
+ if (serviceCalendarFrameObject.serviceCalendar() != null) {
+ // Creating ActiveDates form DayTypeAssignments for OperatingDays for ServiceCalendar
+ activeDatesForOperatingDays(
+ serviceCalendarFrameObject.serviceCalendar().calendarData(),
+ serviceCalendarFrameObject.serviceCalendar().validBetween()
+ );
+ }
+
+ // Creating ActiveDates form DayTypeAssignments for OperatingPeriods for ServiceCalendarFrame
+ activeDatesForOperatingPeriods(
+ serviceCalendarFrameObject.calendarData(),
+ serviceCalendarFrameObject.validBetween()
+ );
+
+ if (serviceCalendarFrameObject.serviceCalendar() != null) {
+ // Creating ActiveDates form DayTypeAssignments for OperatingPeriods for ServiceCalendar
+ activeDatesForOperatingPeriods(
+ serviceCalendarFrameObject.serviceCalendar().calendarData(),
+ serviceCalendarFrameObject.serviceCalendar().validBetween()
+ );
+ }
+
+ return Map.copyOf(activeDatesForDayTypeRef);
+ }
+
+ public Map buildPerOperatingDay(
+ ServiceCalendarFrameObject serviceCalendarFrameObject
+ ) {
+ Map activeDaysPerOperatingDayId =
+ activeDaysPerOperatingDayId(
+ serviceCalendarFrameObject.calendarData(),
+ serviceCalendarFrameObject.validBetween()
+ );
+
+ if (serviceCalendarFrameObject.serviceCalendar() != null) {
+ activeDaysPerOperatingDayId(
+ serviceCalendarFrameObject.serviceCalendar().calendarData(),
+ serviceCalendarFrameObject.serviceCalendar().validBetween()
+ )
+ .forEach(activeDaysPerOperatingDayId::putIfAbsent);
+ }
+
+ return activeDaysPerOperatingDayId;
+ }
+
+ private Map activeDaysPerOperatingDayId(
+ CalendarData calendarData,
+ ValidBetween validBetween
+ ) {
+ return calendarData
+ .operatingDays()
+ .entrySet()
+ .stream()
+ .filter(entry ->
+ isWithinValidRange(entry.getValue().getCalendarDate(), validBetween)
+ )
+ .collect(
+ toMap(
+ Map.Entry::getKey,
+ entry ->
+ new ActiveDates(
+ List.of(entry.getValue().getCalendarDate().toLocalDate())
+ )
+ )
+ );
+ }
+
+ private void addDayType(DayTypeId dayTypeRef, DayType dayType) {
+ if (dayType.getProperties() != null) {
+ for (PropertyOfDay propertyOfDay : dayType
+ .getProperties()
+ .getPropertyOfDay()) {
+ List daysOfWeeks = propertyOfDay.getDaysOfWeek();
+
+ int intDayTypes = 0;
+ for (DayOfWeekEnumeration dayOfWeek : daysOfWeeks) {
+ List dayTypeEnums = convertDayOfWeek(dayOfWeek);
+
+ for (DayOfWeekEnumeration dayTypeEnum : dayTypeEnums) {
+ int mask = 1 << dayTypeEnum.ordinal();
+ intDayTypes |= mask;
+ }
+ }
+ daysForDayTypeId.put(dayTypeRef, intDayTypes);
+ }
+ }
+ }
+
+ private void activeDatesForDates(
+ CalendarData calendarData,
+ ValidBetween validBetween
+ ) {
+ // Dates
+ for (DayTypeId dayTypeId : calendarData.dayTypeAssignments().keySet()) {
+ Collection dayTypeAssignments = calendarData
+ .dayTypeAssignments()
+ .get(dayTypeId);
+ Map> includedAndExcludedDates =
+ findIncludedAndExcludedDates(dayTypeAssignments, validBetween);
+
+ Optional
+ .ofNullable(includedAndExcludedDates.get(Boolean.FALSE))
+ .filter(Predicate.not(List::isEmpty))
+ .ifPresent(
+ excludedDates.computeIfAbsent(dayTypeId, d -> new HashSet<>())::addAll
+ );
+
+ // It should be true here, otherwise error for missing reference should already be reported.
+ // If it's false, it means that the DayTypeAssignment is referring to a dayType that is not defined in the dayTypes.
+ if (activeDatesForDayTypeRef.containsKey(dayTypeId)) {
+ Optional
+ .ofNullable(includedAndExcludedDates.get(Boolean.TRUE))
+ .ifPresent(activeDatesForDayTypeRef.get(dayTypeId).dates()::addAll);
+ }
+ }
+ }
+
+ private void activeDatesForOperatingDays(
+ CalendarData calendarData,
+ ValidBetween validBetween
+ ) {
+ // Operating days
+ for (DayTypeId dayTypeId : calendarData.dayTypeAssignments().keySet()) {
+ Collection dayTypeAssignments = calendarData
+ .dayTypeAssignments()
+ .get(dayTypeId);
+ Map> includedAndExcludedDates =
+ findIncludedAndExcludedOperatingDays(
+ dayTypeAssignments,
+ validBetween,
+ calendarData.operatingDays()
+ );
+
+ Optional
+ .ofNullable(includedAndExcludedDates.get(Boolean.FALSE))
+ .filter(Predicate.not(List::isEmpty))
+ .ifPresent(
+ excludedDates.computeIfAbsent(dayTypeId, d -> new HashSet<>())::addAll
+ );
+
+ // It should be true here, otherwise error for missing reference should already be reported.
+ // If it's false, it means that the DayTypeAssignment is referring to a dayType that is not defined in the dayTypes.
+ if (activeDatesForDayTypeRef.containsKey(dayTypeId)) {
+ Optional
+ .ofNullable(includedAndExcludedDates.get(Boolean.TRUE))
+ .ifPresent(activeDatesForDayTypeRef.get(dayTypeId).dates()::addAll);
+ }
+ }
+ }
+
+ private void activeDatesForOperatingPeriods(
+ CalendarData calendarData,
+ ValidBetween validBetween
+ ) {
+ for (DayTypeId dayTypeId : calendarData.dayTypeAssignments().keys()) {
+ Collection dayTypeAssignments = calendarData
+ .dayTypeAssignments()
+ .get(dayTypeId);
+ dayTypeAssignments.forEach(dayTypeAssignment ->
+ Optional
+ .ofNullable(dayTypeAssignment.getOperatingPeriodRef())
+ .map(JAXBElement::getValue)
+ .map(VersionOfObjectRefStructure::getRef)
+ .map(calendarData.operatingPeriods()::get)
+ .map(operatingPeriod ->
+ ValidOperatingPeriod.of(
+ operatingPeriod,
+ validBetween,
+ calendarData.operatingDays()
+ )
+ )
+ .filter(ValidOperatingPeriod::isValid)
+ .ifPresent(validOperatingPeriod ->
+ activeDatesForDayTypeRef
+ .get(dayTypeId)
+ .dates()
+ .addAll(
+ validOperatingPeriod.toDates(
+ excludedDates.getOrDefault(dayTypeId, Set.of()),
+ daysForDayTypeId.getOrDefault(dayTypeId, 127) // 127 means all days of weak. i.e. 0b01111111
+ )
+ )
+ )
+ );
+ }
+ }
+
+ private static Map> findIncludedAndExcludedDates(
+ Collection dayTypeAssignments,
+ ValidBetween validBetween
+ ) {
+ return dayTypeAssignments
+ .stream()
+ .filter(dayTypeAssignment ->
+ Optional
+ .ofNullable(dayTypeAssignment.getDate())
+ .filter(date -> isWithinValidRange(date, validBetween))
+ .isPresent()
+ )
+ .collect(
+ groupingBy(
+ dayTypeAssignment ->
+ getOrDefault(dayTypeAssignment.isIsAvailable(), Boolean.TRUE),
+ mapping(
+ dayTypeAssignment -> dayTypeAssignment.getDate().toLocalDate(),
+ toList()
+ )
+ )
+ );
+ }
+
+ private Map> findIncludedAndExcludedOperatingDays(
+ Collection dayTypeAssignments,
+ ValidBetween validBetween,
+ Map operatingDays
+ ) {
+ return dayTypeAssignments
+ .stream()
+ .filter(dayTypeAssignment ->
+ Optional
+ .ofNullable(OperatingDayId.of(dayTypeAssignment))
+ .map(operatingDays::get)
+ .map(OperatingDay::getCalendarDate)
+ .filter(dateOfOperation ->
+ isWithinValidRange(dateOfOperation, validBetween)
+ )
+ .isPresent()
+ )
+ .collect(
+ groupingBy(
+ dta -> getOrDefault(dta.isIsAvailable(), Boolean.TRUE),
+ mapping(
+ dta ->
+ Optional
+ .ofNullable(OperatingDayId.of(dta))
+ .map(operatingDays::get)
+ .map(OperatingDay_VersionStructure::getCalendarDate)
+ .map(LocalDateTime::toLocalDate)
+ .orElse(null),
+ toList()
+ )
+ )
+ );
+ }
+
+ private static List convertDayOfWeek(
+ DayOfWeekEnumeration dayOfWeek
+ ) {
+ List days = new ArrayList<>();
+
+ switch (dayOfWeek) {
+ case MONDAY:
+ days.add(DayOfWeekEnumeration.MONDAY);
+ break;
+ case TUESDAY:
+ days.add(DayOfWeekEnumeration.TUESDAY);
+ break;
+ case WEDNESDAY:
+ days.add(DayOfWeekEnumeration.WEDNESDAY);
+ break;
+ case THURSDAY:
+ days.add(DayOfWeekEnumeration.THURSDAY);
+ break;
+ case FRIDAY:
+ days.add(DayOfWeekEnumeration.FRIDAY);
+ break;
+ case SATURDAY:
+ days.add(DayOfWeekEnumeration.SATURDAY);
+ break;
+ case SUNDAY:
+ days.add(DayOfWeekEnumeration.SUNDAY);
+ break;
+ case EVERYDAY:
+ days.add(DayOfWeekEnumeration.MONDAY);
+ days.add(DayOfWeekEnumeration.TUESDAY);
+ days.add(DayOfWeekEnumeration.WEDNESDAY);
+ days.add(DayOfWeekEnumeration.THURSDAY);
+ days.add(DayOfWeekEnumeration.FRIDAY);
+ days.add(DayOfWeekEnumeration.SATURDAY);
+ days.add(DayOfWeekEnumeration.SUNDAY);
+ break;
+ case WEEKDAYS:
+ days.add(DayOfWeekEnumeration.MONDAY);
+ days.add(DayOfWeekEnumeration.TUESDAY);
+ days.add(DayOfWeekEnumeration.WEDNESDAY);
+ days.add(DayOfWeekEnumeration.THURSDAY);
+ days.add(DayOfWeekEnumeration.FRIDAY);
+ break;
+ case WEEKEND:
+ days.add(DayOfWeekEnumeration.SATURDAY);
+ days.add(DayOfWeekEnumeration.SUNDAY);
+ break;
+ case NONE:
+ // None
+ break;
+ }
+ return days;
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarData.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarData.java
new file mode 100644
index 00000000..0f03c81b
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarData.java
@@ -0,0 +1,17 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import com.google.common.collect.Multimap;
+import java.util.Map;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.rutebanken.netex.model.DayType;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingPeriod;
+
+public record CalendarData(
+ Map dayTypes,
+ Map operatingPeriods,
+ Map operatingDays,
+ Multimap dayTypeAssignments
+) {}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilities.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilities.java
new file mode 100644
index 00000000..9420aa5d
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilities.java
@@ -0,0 +1,136 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.time.LocalDateTime;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import org.rutebanken.netex.model.AvailabilityCondition;
+import org.rutebanken.netex.model.Common_VersionFrameStructure;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.ValidBetween;
+import org.rutebanken.netex.model.ValidityConditions_RelStructure;
+
+public class CalendarUtilities {
+
+ static Collector> toMultimap(
+ Function keyMapper,
+ Function valueMapper
+ ) {
+ return Collector.of(
+ ArrayListMultimap::create, // Supplier: Create a new Multimap
+ (multimap, assignment) ->
+ multimap.put(
+ keyMapper.apply(assignment),
+ valueMapper.apply(assignment)
+ ), // Accumulator
+ (m1, m2) -> { // Combiner
+ m1.putAll(m2);
+ return m1;
+ }
+ );
+ }
+
+ static ValidBetween getValidBetween(
+ ValidityConditions_RelStructure validityConditionStruct
+ ) {
+ return Optional
+ .ofNullable(validityConditionStruct)
+ .map(
+ ValidityConditions_RelStructure::getValidityConditionRefOrValidBetweenOrValidityCondition_
+ )
+ .filter(elements -> !elements.isEmpty())
+ .map(elements -> elements.get(0))
+ .flatMap(CalendarUtilities::toValidBetween)
+ .orElse(null);
+ }
+
+ private static Optional toValidBetween(
+ Object validityConditionElement
+ ) {
+ if (validityConditionElement instanceof ValidBetween) {
+ return Optional.of((ValidBetween) validityConditionElement);
+ }
+
+ if (validityConditionElement instanceof javax.xml.bind.JAXBElement>) {
+ return handleJaxbElement(
+ (javax.xml.bind.JAXBElement>) validityConditionElement
+ );
+ }
+
+ throw new RuntimeException(
+ "Only support ValidBetween and AvailabilityCondition as validityCondition"
+ );
+ }
+
+ private static Optional handleJaxbElement(
+ javax.xml.bind.JAXBElement> jaxbElement
+ ) {
+ Object value = jaxbElement.getValue();
+
+ if (value instanceof AvailabilityCondition availabilityCondition) {
+ return Optional.of(
+ new ValidBetween()
+ .withFromDate(availabilityCondition.getFromDate())
+ .withToDate(availabilityCondition.getToDate())
+ );
+ }
+
+ throw new RuntimeException(
+ "Only support ValidBetween and AvailabilityCondition as validityCondition"
+ );
+ }
+
+ public static ValidBetween getValidityForFrameOrDefault(
+ Common_VersionFrameStructure frameStructure,
+ ValidBetween defaultValidity
+ ) {
+ if (frameStructure.getContentValidityConditions() != null) {
+ return getValidBetween(frameStructure.getContentValidityConditions());
+ }
+
+ if (frameStructure.getValidityConditions() != null) {
+ return getValidBetween(frameStructure.getValidityConditions());
+ }
+
+ if (
+ frameStructure.getValidBetween() != null &&
+ !frameStructure.getValidBetween().isEmpty()
+ ) {
+ return frameStructure.getValidBetween().get(0);
+ }
+ return defaultValidity;
+ }
+
+ static boolean isWithinValidRange(
+ LocalDateTime dateOfOperation,
+ ValidBetween validBetween
+ ) {
+ if (validBetween == null) {
+ // Always valid
+ return true;
+ } else if (
+ validBetween.getFromDate() != null && validBetween.getToDate() != null
+ ) {
+ // Limited by both from and to date
+ return (
+ !dateOfOperation.isBefore(validBetween.getFromDate()) &&
+ !dateOfOperation.isAfter(validBetween.getToDate())
+ );
+ } else if (validBetween.getFromDate() != null) {
+ // Must be after valid start date
+ return !dateOfOperation.isBefore(validBetween.getFromDate());
+ } else if (validBetween.getToDate() != null) {
+ // Must be before valid start date
+ return dateOfOperation.isBefore(validBetween.getToDate());
+ } else {
+ // Both from and to empty
+ return true;
+ }
+ }
+
+ static V getOrDefault(V value, V defaultValue) {
+ return value != null ? value : defaultValue;
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObject.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObject.java
new file mode 100644
index 00000000..81564c2d
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObject.java
@@ -0,0 +1,133 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.getValidityForFrameOrDefault;
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.toMultimap;
+
+import com.google.common.collect.Multimap;
+import jakarta.xml.bind.JAXBElement;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import no.entur.antu.exception.AntuException;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.rutebanken.netex.model.DayType;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.DayTypeAssignmentsInFrame_RelStructure;
+import org.rutebanken.netex.model.DayTypesInFrame_RelStructure;
+import org.rutebanken.netex.model.EntityStructure;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingDaysInFrame_RelStructure;
+import org.rutebanken.netex.model.OperatingPeriod;
+import org.rutebanken.netex.model.OperatingPeriodsInFrame_RelStructure;
+import org.rutebanken.netex.model.ServiceCalendarFrame;
+import org.rutebanken.netex.model.ValidBetween;
+
+public record ServiceCalendarFrameObject(
+ ValidBetween validBetween,
+ CalendarData calendarData,
+ ServiceCalendarObject serviceCalendar
+) {
+ public static ServiceCalendarFrameObject ofNullable(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ return ofNullable(serviceCalendarFrame, null);
+ }
+
+ public static ServiceCalendarFrameObject ofNullable(
+ ServiceCalendarFrame serviceCalendarFrame,
+ ValidBetween compositeFrameValidity
+ ) {
+ if (serviceCalendarFrame == null) {
+ throw new AntuException(
+ "ServiceCalendarFrame_VersionFrameStructure is null"
+ );
+ }
+ ValidBetween serviceCalendarFrameValidity = getValidityForFrameOrDefault(
+ serviceCalendarFrame,
+ compositeFrameValidity
+ );
+ return new ServiceCalendarFrameObject(
+ serviceCalendarFrameValidity,
+ new CalendarData(
+ getDayTypes(serviceCalendarFrame),
+ getOperatingPeriods(serviceCalendarFrame),
+ getOperatingDays(serviceCalendarFrame),
+ getDayTypeAssignmentByDayTypeId(serviceCalendarFrame)
+ ),
+ ServiceCalendarObject.ofNullable(
+ serviceCalendarFrame.getServiceCalendar(),
+ serviceCalendarFrameValidity
+ )
+ );
+ }
+
+ private static Multimap getDayTypeAssignmentByDayTypeId(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ return Optional
+ .ofNullable(serviceCalendarFrame.getDayTypeAssignments())
+ .map(DayTypeAssignmentsInFrame_RelStructure::getDayTypeAssignment)
+ .stream()
+ .flatMap(Collection::stream)
+ .collect(toMultimap(DayTypeId::of, Function.identity()));
+ }
+
+ private static Map getOperatingDays(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ return Optional
+ .ofNullable(serviceCalendarFrame.getOperatingDays())
+ .map(OperatingDaysInFrame_RelStructure::getOperatingDay)
+ .stream()
+ .flatMap(Collection::stream)
+ .collect(Collectors.toMap(OperatingDayId::of, Function.identity()));
+ }
+
+ private static Map getOperatingPeriods(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ return Optional
+ .ofNullable(serviceCalendarFrame.getOperatingPeriods())
+ .map(
+ OperatingPeriodsInFrame_RelStructure::getOperatingPeriodOrUicOperatingPeriod
+ )
+ .stream()
+ .flatMap(List::stream)
+ .filter(OperatingPeriod.class::isInstance)
+ .map(OperatingPeriod.class::cast)
+ .collect(Collectors.toMap(EntityStructure::getId, Function.identity()));
+ }
+
+ private static Map getDayTypes(
+ ServiceCalendarFrame serviceCalendarFrame
+ ) {
+ return Optional
+ .ofNullable(serviceCalendarFrame.getDayTypes())
+ .map(ServiceCalendarFrameObject::parseDayTypes)
+ .stream()
+ .flatMap(List::stream)
+ .filter(dayType -> DayTypeId.isValid(dayType.getId()))
+ .collect(
+ Collectors.toMap(
+ dayType -> new DayTypeId(dayType.getId()),
+ Function.identity()
+ )
+ );
+ }
+
+ private static List parseDayTypes(
+ DayTypesInFrame_RelStructure element
+ ) {
+ return element
+ .getDayType_()
+ .stream()
+ .map(JAXBElement::getValue)
+ .filter(DayType.class::isInstance)
+ .map(DayType.class::cast)
+ .toList();
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarObject.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarObject.java
new file mode 100644
index 00000000..56c59f3d
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarObject.java
@@ -0,0 +1,168 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.getValidBetween;
+import static no.entur.antu.netexdata.collectors.activedatecollector.calender.CalendarUtilities.toMultimap;
+
+import com.google.common.collect.Multimap;
+import jakarta.xml.bind.JAXBElement;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.rutebanken.netex.model.DayType;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.DayTypeAssignments_RelStructure;
+import org.rutebanken.netex.model.DayTypes_RelStructure;
+import org.rutebanken.netex.model.EntityStructure;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingDays_RelStructure;
+import org.rutebanken.netex.model.OperatingPeriod;
+import org.rutebanken.netex.model.OperatingPeriod_VersionStructure;
+import org.rutebanken.netex.model.OperatingPeriods_RelStructure;
+import org.rutebanken.netex.model.ServiceCalendar;
+import org.rutebanken.netex.model.ValidBetween;
+
+public record ServiceCalendarObject(
+ ValidBetween validBetween,
+ CalendarData calendarData
+) {
+ static ServiceCalendarObject ofNullable(
+ ServiceCalendar serviceCalendar,
+ ValidBetween serviceCalendarFrameValidity
+ ) {
+ if (serviceCalendar == null) {
+ return null;
+ }
+ return new ServiceCalendarObject(
+ getServiceCalendarValidity(serviceCalendar, serviceCalendarFrameValidity),
+ new CalendarData(
+ getDayTypes(serviceCalendar),
+ getOperatingPeriods(serviceCalendar),
+ getOperatingDays(serviceCalendar),
+ getDayTypeAssignmentByDayTypeId(serviceCalendar)
+ )
+ );
+ }
+
+ private static Multimap getDayTypeAssignmentByDayTypeId(
+ ServiceCalendar serviceCalendar
+ ) {
+ return Optional
+ .ofNullable(serviceCalendar.getDayTypeAssignments())
+ .map(DayTypeAssignments_RelStructure::getDayTypeAssignment)
+ .stream()
+ .flatMap(Collection::stream)
+ .collect(toMultimap(DayTypeId::of, Function.identity()));
+ }
+
+ private static Map getOperatingDays(
+ ServiceCalendar serviceCalendar
+ ) {
+ return Optional
+ .ofNullable(serviceCalendar.getOperatingDays())
+ .map(OperatingDays_RelStructure::getOperatingDayRefOrOperatingDay)
+ .stream()
+ .flatMap(Collection::stream)
+ .filter(OperatingDay.class::isInstance)
+ .map(OperatingDay.class::cast)
+ .collect(Collectors.toMap(OperatingDayId::of, Function.identity()));
+ }
+
+ private static Map getOperatingPeriods(
+ ServiceCalendar serviceCalendar
+ ) {
+ return Optional
+ .ofNullable(serviceCalendar.getOperatingPeriods())
+ .map(ServiceCalendarObject::parseOperatingPeriods)
+ .stream()
+ .flatMap(List::stream)
+ .filter(OperatingPeriod.class::isInstance)
+ .map(OperatingPeriod.class::cast)
+ .collect(Collectors.toMap(EntityStructure::getId, Function.identity()));
+ }
+
+ private static Map getDayTypes(
+ ServiceCalendar serviceCalendar
+ ) {
+ return Optional
+ .ofNullable(serviceCalendar.getDayTypes())
+ .map(ServiceCalendarObject::parseDayTypes)
+ .stream()
+ .flatMap(List::stream)
+ .filter(dayType -> DayTypeId.isValid(dayType.getId()))
+ .collect(
+ Collectors.toMap(
+ dayType -> new DayTypeId(dayType.getId()),
+ Function.identity()
+ )
+ );
+ }
+
+ private static ValidBetween getServiceCalendarValidity(
+ ServiceCalendar serviceCalendar,
+ ValidBetween serviceCalendarFrameValidity
+ ) {
+ if (
+ serviceCalendar.getFromDate() != null &&
+ serviceCalendar.getToDate() != null
+ ) {
+ LocalDateTime fromDateTime = serviceCalendar.getFromDate();
+ LocalDateTime toDateTime = serviceCalendar.getToDate();
+ return new ValidBetween()
+ .withFromDate(fromDateTime)
+ .withToDate(toDateTime);
+ } else {
+ ValidBetween entityValidity = getValidBetweenForServiceCalendar(
+ serviceCalendar
+ );
+ if (entityValidity != null) {
+ return entityValidity;
+ }
+ return serviceCalendarFrameValidity;
+ }
+ }
+
+ static ValidBetween getValidBetweenForServiceCalendar(
+ ServiceCalendar entityStruct
+ ) {
+ ValidBetween validBetween = null;
+
+ if (entityStruct.getValidityConditions() != null) {
+ validBetween = getValidBetween(entityStruct.getValidityConditions());
+ } else if (
+ entityStruct.getValidBetween() != null &&
+ !entityStruct.getValidBetween().isEmpty()
+ ) {
+ validBetween = entityStruct.getValidBetween().get(0);
+ }
+
+ return validBetween;
+ }
+
+ private static List parseDayTypes(DayTypes_RelStructure dayTypes) {
+ return dayTypes
+ .getDayTypeRefOrDayType_()
+ .stream()
+ .map(JAXBElement::getValue)
+ .filter(DayType.class::isInstance)
+ .map(DayType.class::cast)
+ .toList();
+ }
+
+ private static List parseOperatingPeriods(
+ OperatingPeriods_RelStructure operatingPeriods
+ ) {
+ return operatingPeriods
+ .getOperatingPeriodRefOrOperatingPeriodOrUicOperatingPeriod()
+ .stream()
+ .map(JAXBElement::getValue)
+ .filter(OperatingPeriod_VersionStructure.class::isInstance)
+ .map(OperatingPeriod_VersionStructure.class::cast)
+ .toList();
+ }
+}
diff --git a/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriod.java b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriod.java
new file mode 100644
index 00000000..036345ac
--- /dev/null
+++ b/src/main/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriod.java
@@ -0,0 +1,117 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingPeriod;
+import org.rutebanken.netex.model.ValidBetween;
+
+record ValidOperatingPeriod(LocalDate startDate, LocalDate endDate) {
+ static ValidOperatingPeriod of(
+ OperatingPeriod operatingPeriod,
+ ValidBetween validBetween,
+ Map operatingDays
+ ) {
+ LocalDate fromDate = getFromDate(operatingPeriod, operatingDays);
+ LocalDate toDate = getToDate(operatingPeriod, operatingDays);
+
+ return cutToValidityCondition(fromDate, toDate, validBetween);
+ }
+
+ private static LocalDate getFromDate(
+ OperatingPeriod operatingPeriod,
+ Map operatingDays
+ ) {
+ return Optional
+ .ofNullable(OperatingDayId.ofFromOperatingDayRef(operatingPeriod))
+ .map(operatingDays::get)
+ .map(OperatingDay::getCalendarDate)
+ .map(LocalDateTime::toLocalDate)
+ .orElseGet(() -> operatingPeriod.getFromDate().toLocalDate());
+ }
+
+ private static LocalDate getToDate(
+ OperatingPeriod operatingPeriod,
+ Map operatingDays
+ ) {
+ return Optional
+ .ofNullable(OperatingDayId.ofToOperatingDayRef(operatingPeriod))
+ .map(operatingDays::get)
+ .map(OperatingDay::getCalendarDate)
+ .map(LocalDateTime::toLocalDate)
+ .orElseGet(() -> operatingPeriod.getToDate().toLocalDate());
+ }
+
+ // Adjust operating period to validity condition
+ private static ValidOperatingPeriod cutToValidityCondition(
+ LocalDate startDate,
+ LocalDate endDate,
+ ValidBetween validBetween
+ ) {
+ if (validBetween == null) {
+ return new ValidOperatingPeriod(startDate, endDate);
+ }
+
+ LocalDate validFrom = validBetween.getFromDate() != null
+ ? validBetween.getFromDate().toLocalDate()
+ : null;
+ LocalDate validTo = validBetween.getToDate() != null
+ ? validBetween.getToDate().toLocalDate()
+ : null;
+
+ // Check if the period is completely outside the valid range
+ if (
+ (validFrom != null && endDate.isBefore(validFrom)) ||
+ (validTo != null && startDate.isAfter(validTo))
+ ) {
+ return new ValidOperatingPeriod(null, null);
+ }
+
+ // Adjust the start and end dates to be within the valid range
+ LocalDate adjustedStart = (
+ validFrom != null && startDate.isBefore(validFrom)
+ )
+ ? validFrom
+ : startDate;
+ LocalDate adjustedEnd = (validTo != null && endDate.isAfter(validTo))
+ ? validTo
+ : endDate;
+
+ return new ValidOperatingPeriod(adjustedStart, adjustedEnd);
+ }
+
+ List toDates(Set excludedDates, int intDayTypes) {
+ if (!isValid()) {
+ return List.of();
+ }
+
+ List dates = new ArrayList<>();
+
+ if (intDayTypes != 0) {
+ LocalDate date = startDate;
+
+ while (!date.isAfter(endDate)) {
+ int aDayOfWeek = date.getDayOfWeek().getValue() - 1;
+ int aDayOfWeekFlag = 1 << aDayOfWeek;
+ if ((intDayTypes & aDayOfWeekFlag) == aDayOfWeekFlag) {
+ if (excludedDates == null || !excludedDates.contains(date)) {
+ dates.add(date);
+ }
+ }
+ date = date.plusDays(1);
+ }
+ }
+ return dates;
+ }
+
+ public boolean isValid() {
+ return startDate != null && endDate != null;
+ }
+}
diff --git a/src/main/java/no/entur/antu/sweden/validator/EnturTimetableDataSwedenValidationTreeFactory.java b/src/main/java/no/entur/antu/sweden/validator/EnturTimetableDataSwedenValidationTreeFactory.java
index 8508f786..139cfe6a 100644
--- a/src/main/java/no/entur/antu/sweden/validator/EnturTimetableDataSwedenValidationTreeFactory.java
+++ b/src/main/java/no/entur/antu/sweden/validator/EnturTimetableDataSwedenValidationTreeFactory.java
@@ -33,7 +33,9 @@ public EnturTimetableDataSwedenValidationTreeFactory() {
@Override
public ValidationTreeBuilder builder() {
- // accept SiteFrame, they are part of Finnish datasets
+ ValidationTreeBuilder builder = super.builder();
+
+ // accept SiteFrame, they are part of swedish datasets
siteFrameValidationTreeBuilder()
.removeRuleForCommonFile(
DefaultSiteFrameValidationTreeFactory.CODE_SITE_FRAME_IN_COMMON_FILE
@@ -62,7 +64,7 @@ public ValidationTreeBuilder builder() {
.removeRule(
DefaultCompositeFrameTreeFactory.CODE_COMPOSITE_FRAME_SITE_FRAME
)
- .removeRuleForCommonFile(CODE_COMPOSITE_FRAME_1)
+ .removeRule(CODE_COMPOSITE_FRAME_1)
.withRule(
new ValidateNotExist(
".[not(validityConditions or ValidBetween)]",
@@ -80,6 +82,6 @@ public ValidationTreeBuilder builder() {
.removeRule(
DefaultServiceFrameValidationTreeFactory.CODE_PASSENGER_STOP_ASSIGNMENT_3
);
- return super.builder();
+ return builder;
}
}
diff --git a/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesContext.java b/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesContext.java
new file mode 100644
index 00000000..1f3a765d
--- /dev/null
+++ b/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesContext.java
@@ -0,0 +1,144 @@
+package no.entur.antu.validation.validator.interchange.waittime;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import org.entur.netex.validation.validator.jaxb.NetexDataRepository;
+import org.entur.netex.validation.validator.model.ActiveDates;
+import org.entur.netex.validation.validator.model.ActiveDatesId;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.entur.netex.validation.validator.model.ScheduledStopPointId;
+import org.entur.netex.validation.validator.model.ServiceJourneyId;
+import org.entur.netex.validation.validator.model.ServiceJourneyInterchangeInfo;
+import org.entur.netex.validation.validator.model.ServiceJourneyStop;
+
+public record UnexpectedWaitTimeAndActiveDatesContext(
+ ServiceJourneyInterchangeInfo serviceJourneyInterchangeInfo,
+ // ServiceJourneyStop at the fromStopPoint in fromJourneyRef from Cache
+ ServiceJourneyStop fromServiceJourneyStop,
+ // ServiceJourneySStop at the toStopPoint in toJourneyRef from Cache
+ ServiceJourneyStop toServiceJourneyStop,
+ // Active dates for the fromJourneyRef from Cache
+ List fromServiceJourneyActiveDates,
+ // Active dates for the toJourneyRef from Cache
+ List toServiceJourneyActiveDates
+) {
+ public static class Builder {
+
+ private final String validationReportId;
+ private final NetexDataRepository netexDataRepository;
+ private Map> serviceJourneyIdListMap;
+ private Map> serviceJourneyDayTypesMap;
+ private Map activeDatesMap;
+ private Map> serviceJourneyOperatingDaysMap;
+
+ public Builder(
+ String validationReportId,
+ NetexDataRepository netexDataRepository
+ ) {
+ this.validationReportId = validationReportId;
+ this.netexDataRepository = netexDataRepository;
+ }
+
+ public UnexpectedWaitTimeAndActiveDatesContext.Builder primeCache() {
+ serviceJourneyIdListMap =
+ netexDataRepository.serviceJourneyStops(validationReportId);
+ serviceJourneyDayTypesMap =
+ netexDataRepository.serviceJourneyDayTypes(validationReportId);
+ activeDatesMap = netexDataRepository.activeDates(validationReportId);
+ serviceJourneyOperatingDaysMap =
+ netexDataRepository.serviceJourneyOperatingDays(validationReportId);
+ return this;
+ }
+
+ public UnexpectedWaitTimeAndActiveDatesContext build(
+ ServiceJourneyInterchangeInfo serviceJourneyInterchangeInfo
+ ) {
+ return new UnexpectedWaitTimeAndActiveDatesContext(
+ serviceJourneyInterchangeInfo,
+ serviceJourneyStopAtScheduleStopPoint(
+ serviceJourneyInterchangeInfo.fromJourneyRef(),
+ serviceJourneyInterchangeInfo.fromStopPoint()
+ ),
+ serviceJourneyStopAtScheduleStopPoint(
+ serviceJourneyInterchangeInfo.toJourneyRef(),
+ serviceJourneyInterchangeInfo.toStopPoint()
+ ),
+ activeDatesForServiceJourney(
+ serviceJourneyInterchangeInfo.fromJourneyRef()
+ ),
+ activeDatesForServiceJourney(
+ serviceJourneyInterchangeInfo.toJourneyRef()
+ )
+ );
+ }
+
+ private List activeDatesForServiceJourney(
+ ServiceJourneyId serviceJourneyId
+ ) {
+ List activeDateOfDayTypes = Optional
+ .ofNullable(serviceJourneyId)
+ .map(serviceJourneyDayTypesMap::get)
+ .map(dayTypeIds ->
+ dayTypeIds
+ .stream()
+ .map(activeDatesMap::get)
+ .map(ActiveDates::dates)
+ .flatMap(List::stream)
+ .toList()
+ )
+ .orElse(List.of());
+
+ List activeDateOfDatedServiceJourneys = Optional
+ .ofNullable(serviceJourneyId)
+ .map(serviceJourneyOperatingDaysMap::get)
+ .map(dayTypeIds ->
+ dayTypeIds
+ .stream()
+ .map(activeDatesMap::get)
+ .map(ActiveDates::dates)
+ .flatMap(List::stream)
+ .toList()
+ )
+ .orElse(List.of());
+
+ return Stream
+ .of(activeDateOfDayTypes, activeDateOfDatedServiceJourneys)
+ .flatMap(List::stream)
+ .toList();
+ }
+
+ private ServiceJourneyStop serviceJourneyStopAtScheduleStopPoint(
+ ServiceJourneyId serviceJourneyId,
+ ScheduledStopPointId scheduledStopPointId
+ ) {
+ return Optional
+ .ofNullable(serviceJourneyId)
+ .map(serviceJourneyIdListMap::get)
+ .flatMap(serviceJourneyStops ->
+ serviceJourneyStops
+ .stream()
+ .filter(serviceJourneyStop ->
+ serviceJourneyStop
+ .scheduledStopPointId()
+ .equals(scheduledStopPointId)
+ )
+ .map(ServiceJourneyStop::fixMissingTimeValues)
+ .findFirst()
+ )
+ .orElse(null);
+ }
+ }
+
+ public boolean isValid() {
+ return (
+ serviceJourneyInterchangeInfo != null &&
+ serviceJourneyInterchangeInfo.isValid() &&
+ fromServiceJourneyStop != null &&
+ toServiceJourneyStop != null
+ );
+ }
+}
diff --git a/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesValidator.java b/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesValidator.java
new file mode 100644
index 00000000..8cf7253a
--- /dev/null
+++ b/src/main/java/no/entur/antu/validation/validator/interchange/waittime/UnexpectedWaitTimeAndActiveDatesValidator.java
@@ -0,0 +1,228 @@
+package no.entur.antu.validation.validator.interchange.waittime;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import no.entur.antu.validation.utilities.Comparison;
+import org.entur.netex.validation.validator.AbstractDatasetValidator;
+import org.entur.netex.validation.validator.DataLocation;
+import org.entur.netex.validation.validator.Severity;
+import org.entur.netex.validation.validator.ValidationIssue;
+import org.entur.netex.validation.validator.ValidationReport;
+import org.entur.netex.validation.validator.ValidationReportEntry;
+import org.entur.netex.validation.validator.ValidationReportEntryFactory;
+import org.entur.netex.validation.validator.ValidationRule;
+import org.entur.netex.validation.validator.jaxb.NetexDataRepository;
+import org.entur.netex.validation.validator.model.ServiceJourneyInterchangeInfo;
+import org.entur.netex.validation.validator.model.ServiceJourneyStop;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verify that wait time is not above configured threshold.
+ * Must check that the two vehicle journeys share at least one active date,
+ * or in the case of interchanges around midnight; consecutive dates.
+ * Chouette reference:
+ * 3-Interchange-8-1,
+ * 3-Interchange-8-2,
+ * 3-Interchange-10
+ */
+public class UnexpectedWaitTimeAndActiveDatesValidator
+ extends AbstractDatasetValidator {
+
+ static final ValidationRule RULE_NO_SHARED_ACTIVE_DATE_FOUND_IN_INTERCHANGE =
+ new ValidationRule(
+ "NO_SHARED_ACTIVE_DATE_FOUND_IN_INTERCHANGE",
+ "No shared active date found in interchange",
+ "No shared active date found in interchange between %s and %s",
+ Severity.WARNING
+ );
+
+ static final ValidationRule RULE_WAIT_TIME_IN_INTERCHANGE_EXCEEDS_WARNING_LIMIT =
+ new ValidationRule(
+ "WAIT_TIME_IN_INTERCHANGE_EXCEEDS_WARNING_LIMIT",
+ "Wait time in interchange exceeds warning limit",
+ "Wait time between stops (%s) and (%s) is expected %s sec. but was %s sec.",
+ Severity.WARNING
+ );
+
+ static final ValidationRule RULE_WAIT_TIME_IN_INTERCHANGE_EXCEEDS_MAX_LIMIT =
+ new ValidationRule(
+ "WAIT_TIME_IN_INTERCHANGE_EXCEEDS_MAX_LIMIT",
+ "Wait time in interchange exceeds maximum limit",
+ "Wait time between stops (%s) and (%s) is expected %s sec. but was %s sec.",
+ Severity.WARNING
+ );
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(
+ UnexpectedWaitTimeAndActiveDatesValidator.class
+ );
+
+ // Marduk/Chouette config parameter: interchange_max_wait_seconds = 3600 Seconds
+
+ // Warning wait time for interchange is 1 hour
+ private static final int INTERCHANGE_WARNING_WAIT_TIME_MILLIS = 3600000; // 1 Hour
+
+ // Maximum wait time for interchange is 3 hours
+ private static final int INTERCHANGE_ERROR_WAIT_TIME_MILLIS =
+ INTERCHANGE_WARNING_WAIT_TIME_MILLIS * 3; // 3 Hours
+
+ private final NetexDataRepository netexDataRepository;
+
+ public UnexpectedWaitTimeAndActiveDatesValidator(
+ ValidationReportEntryFactory validationReportEntryFactory,
+ NetexDataRepository netexDataRepository
+ ) {
+ super(validationReportEntryFactory);
+ this.netexDataRepository = netexDataRepository;
+ }
+
+ @Override
+ public ValidationReport validate(ValidationReport validationReport) {
+ LOGGER.info("Validating interchange wait time.");
+
+ List serviceJourneyInterchangeInfos =
+ netexDataRepository.serviceJourneyInterchangeInfos(
+ validationReport.getValidationReportId()
+ );
+
+ if (
+ serviceJourneyInterchangeInfos == null ||
+ serviceJourneyInterchangeInfos.isEmpty()
+ ) {
+ return validationReport;
+ }
+
+ UnexpectedWaitTimeAndActiveDatesContext.Builder builder =
+ new UnexpectedWaitTimeAndActiveDatesContext.Builder(
+ validationReport.getValidationReportId(),
+ netexDataRepository
+ );
+
+ builder.primeCache();
+
+ serviceJourneyInterchangeInfos
+ .stream()
+ .map(builder::build)
+ .filter(Objects::nonNull)
+ .filter(UnexpectedWaitTimeAndActiveDatesContext::isValid)
+ .map(this::validateWaitTime)
+ .filter(Objects::nonNull)
+ .forEach(validationReport::addValidationReportEntry);
+
+ return validationReport;
+ }
+
+ private ValidationReportEntry validateWaitTime(
+ UnexpectedWaitTimeAndActiveDatesContext context
+ ) {
+ long millisPerDay = 86400000L;
+
+ int dayOffsetDiff =
+ context.toServiceJourneyStop().departureDayOffset() -
+ context.fromServiceJourneyStop().arrivalDayOffset();
+
+ long msWait =
+ (
+ Optional
+ .ofNullable(context.toServiceJourneyStop().departureTime())
+ .map(LocalTime::toSecondOfDay)
+ .orElse(0) -
+ Optional
+ .ofNullable(context.fromServiceJourneyStop().arrivalTime())
+ .map(LocalTime::toSecondOfDay)
+ .orElse(0)
+ ) *
+ 1000L;
+
+ if (msWait < 0) {
+ msWait = millisPerDay + msWait;
+ dayOffsetDiff--;
+ }
+
+ if (!hasSharedActiveDate(context, dayOffsetDiff)) {
+ return createValidationReportEntry(
+ new ValidationIssue(
+ RULE_NO_SHARED_ACTIVE_DATE_FOUND_IN_INTERCHANGE,
+ new DataLocation(
+ context.serviceJourneyInterchangeInfo().interchangeId(),
+ context.serviceJourneyInterchangeInfo().filename(),
+ 0,
+ 0
+ ),
+ context.fromServiceJourneyStop(),
+ context.toServiceJourneyStop()
+ )
+ );
+ } else if (msWait > INTERCHANGE_WARNING_WAIT_TIME_MILLIS) {
+ if (msWait > INTERCHANGE_ERROR_WAIT_TIME_MILLIS) {
+ return createValidationReportEntry(
+ RULE_WAIT_TIME_IN_INTERCHANGE_EXCEEDS_MAX_LIMIT,
+ context.serviceJourneyInterchangeInfo().interchangeId(),
+ context.serviceJourneyInterchangeInfo().filename(),
+ context.fromServiceJourneyStop(),
+ context.toServiceJourneyStop(),
+ Comparison.of(
+ String.valueOf(msWait / 1000),
+ String.valueOf(INTERCHANGE_ERROR_WAIT_TIME_MILLIS / 1000)
+ )
+ );
+ } else {
+ return createValidationReportEntry(
+ RULE_WAIT_TIME_IN_INTERCHANGE_EXCEEDS_WARNING_LIMIT,
+ context.serviceJourneyInterchangeInfo().interchangeId(),
+ context.serviceJourneyInterchangeInfo().filename(),
+ context.fromServiceJourneyStop(),
+ context.toServiceJourneyStop(),
+ Comparison.of(
+ String.valueOf(msWait / 1000),
+ String.valueOf(INTERCHANGE_WARNING_WAIT_TIME_MILLIS / 1000)
+ )
+ );
+ }
+ }
+ return null;
+ }
+
+ private boolean hasSharedActiveDate(
+ UnexpectedWaitTimeAndActiveDatesContext context,
+ int daysOffset
+ ) {
+ List fromServiceJourneyActiveDates =
+ context.fromServiceJourneyActiveDates();
+ for (LocalDate toServiceJourneyActiveDate : context.toServiceJourneyActiveDates()) {
+ LocalDate toServiceJourneyActiveDateWithOffset =
+ toServiceJourneyActiveDate.plusDays(daysOffset);
+ if (
+ fromServiceJourneyActiveDates.contains(
+ toServiceJourneyActiveDateWithOffset
+ )
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ValidationReportEntry createValidationReportEntry(
+ ValidationRule rule,
+ String interchangeId,
+ String filename,
+ ServiceJourneyStop fromJourneyStop,
+ ServiceJourneyStop toJourneyStop,
+ Comparison comparison
+ ) {
+ return createValidationReportEntry(
+ new ValidationIssue(
+ rule,
+ new DataLocation(interchangeId, filename, 0, 0),
+ fromJourneyStop.scheduledStopPointId(),
+ toJourneyStop.scheduledStopPointId(),
+ comparison.expected(),
+ comparison.actual()
+ )
+ );
+ }
+}
diff --git a/src/main/java/no/entur/antu/validation/validator/servicelink/distance/UnexpectedDistanceInServiceLinkContext.java b/src/main/java/no/entur/antu/validation/validator/servicelink/distance/UnexpectedDistanceInServiceLinkContext.java
index c69da173..79e580cd 100644
--- a/src/main/java/no/entur/antu/validation/validator/servicelink/distance/UnexpectedDistanceInServiceLinkContext.java
+++ b/src/main/java/no/entur/antu/validation/validator/servicelink/distance/UnexpectedDistanceInServiceLinkContext.java
@@ -54,7 +54,7 @@ public UnexpectedDistanceInServiceLinkContext build(
Optional lineString = mapServiceLinkToLineString(serviceLink);
if (lineString.isEmpty()) {
- LOGGER.warn(
+ LOGGER.info(
"Could not map serviceLink {} to a LineString",
serviceLink.getId()
);
@@ -121,7 +121,7 @@ private Optional mapServiceLinkToLineString(
private List getCoordinates(LineStringType lineString) {
List coordinates = GeometryUtilities.getCoordinates(lineString);
if (coordinates.isEmpty()) {
- LOGGER.warn("LineString with no coordinates: {}", lineString.getId());
+ LOGGER.info("LineString with no coordinates: {}", lineString.getId());
}
return coordinates;
}
diff --git a/src/main/java/no/entur/antu/validation/validator/support/NetexUtils.java b/src/main/java/no/entur/antu/validation/validator/support/NetexUtils.java
index 4d92e9bd..de9c3def 100644
--- a/src/main/java/no/entur/antu/validation/validator/support/NetexUtils.java
+++ b/src/main/java/no/entur/antu/validation/validator/support/NetexUtils.java
@@ -6,10 +6,12 @@
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
import org.entur.netex.validation.validator.model.ScheduledStopPointId;
import org.rutebanken.netex.model.JourneyPattern;
import org.rutebanken.netex.model.PointInLinkSequence_VersionedChildStructure;
import org.rutebanken.netex.model.PointsInJourneyPattern_RelStructure;
+import org.rutebanken.netex.model.ServiceJourney;
import org.rutebanken.netex.model.StopPointInJourneyPattern;
import org.rutebanken.netex.model.TimetabledPassingTime;
@@ -88,4 +90,30 @@ public static StopPointInJourneyPattern stopPointInJourneyPattern(
.findFirst()
.orElse(null);
}
+
+ /**
+ * Returns the Stream of all the valid ServiceJourneys in all the TimeTableFrames.
+ * The valid serviceJourneys are those that have number of timetabledPassingTime equals to number of StopPointsInJourneyPattern.
+ * This is validated with SERVICE_JOURNEY_10.
+ */
+ public static List validServiceJourneys(
+ JAXBValidationContext validationContext
+ ) {
+ return validationContext
+ .serviceJourneys()
+ .stream()
+ .filter(serviceJourney -> {
+ JourneyPattern journeyPattern = validationContext.journeyPattern(
+ serviceJourney
+ );
+ if (journeyPattern == null) {
+ return false;
+ }
+ return (
+ stopPointsInJourneyPattern(journeyPattern).size() ==
+ validationContext.timetabledPassingTimes(serviceJourney).size()
+ );
+ })
+ .toList();
+ }
}
diff --git a/src/main/java/no/entur/antu/validation/validator/xpath/rules/ValidateAllowedCodespaces.java b/src/main/java/no/entur/antu/validation/validator/xpath/rules/ValidateAllowedCodespaces.java
index 499bc8a3..4f18e22b 100644
--- a/src/main/java/no/entur/antu/validation/validator/xpath/rules/ValidateAllowedCodespaces.java
+++ b/src/main/java/no/entur/antu/validation/validator/xpath/rules/ValidateAllowedCodespaces.java
@@ -1,6 +1,6 @@
package no.entur.antu.validation.validator.xpath.rules;
-import static org.entur.netex.validation.xml.NetexXMLParser.*;
+import static org.entur.netex.validation.xml.NetexXMLParser.NETEX_NAMESPACE;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/main/resources/configuration.antu.yaml b/src/main/resources/configuration.antu.yaml
index cae0e0aa..173861b7 100644
--- a/src/main/resources/configuration.antu.yaml
+++ b/src/main/resources/configuration.antu.yaml
@@ -129,3 +129,12 @@ validationRuleConfigs:
- code: TO_POINT_REF_IN_INTERCHANGE_IS_NOT_PART_OF_TO_JOURNEY_REF
name: ToPointRef in interchange is not a part of ToJourneyRef
severity: WARNING
+ - code: NO_SHARED_ACTIVE_DATE_FOUND_IN_INTERCHANGE
+ name: No shared active date found in interchange
+ severity: WARNING
+ - code: WAIT_TIME_IN_INTERCHANGE_EXCEEDS_WARNING_LIMIT
+ name: Wait time in interchange exceeds warning limit
+ severity: WARNING
+ - code: WAIT_TIME_IN_INTERCHANGE_EXCEEDS_MAX_LIMIT
+ name: Wait time in interchange exceeds maximum limit
+ severity: WARNING
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollectorTest.java b/src/test/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollectorTest.java
new file mode 100644
index 00000000..166a208c
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/DatedServiceJourneysCollectorTest.java
@@ -0,0 +1,117 @@
+package no.entur.antu.netexdata.collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+import java.util.Map;
+import java.util.stream.IntStream;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.index.api.NetexEntitiesIndex;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.junit.jupiter.api.Test;
+
+class DatedServiceJourneysCollectorTest {
+
+ @Test
+ void testOneDatedServiceJourneyPerServiceJourney() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ IntStream
+ .rangeClosed(1, 3)
+ .forEach(i ->
+ netexEntitiesTestFactory.createDatedServiceJourney(
+ i,
+ netexEntitiesTestFactory.createServiceJourney(
+ i,
+ netexEntitiesTestFactory.createJourneyPattern(i)
+ ),
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame()
+ .createOperatingDay(i, LocalDate.of(2024, 1, 1))
+ )
+ );
+
+ Map operatingDaysPerServiceJourney =
+ DatedServiceJourneysCollector.getOperatingDaysPerServiceJourneyIsAsStrings(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(3, operatingDaysPerServiceJourney.size());
+ }
+
+ @Test
+ void testMultipleDatedServiceJourneysPerServiceJourney() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceJourney serviceJourney1 =
+ netexEntitiesTestFactory.createServiceJourney(
+ 1,
+ netexEntitiesTestFactory.createJourneyPattern(1)
+ );
+ IntStream
+ .rangeClosed(1, 3)
+ .forEach(i ->
+ netexEntitiesTestFactory.createDatedServiceJourney(
+ i,
+ serviceJourney1,
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame()
+ .createOperatingDay(i, LocalDate.of(2024, 1, 1 + i))
+ )
+ );
+
+ NetexEntitiesTestFactory.CreateServiceJourney serviceJourney2 =
+ netexEntitiesTestFactory.createServiceJourney(
+ 2,
+ netexEntitiesTestFactory.createJourneyPattern(2)
+ );
+ IntStream
+ .rangeClosed(4, 5)
+ .forEach(i ->
+ netexEntitiesTestFactory.createDatedServiceJourney(
+ i,
+ serviceJourney2,
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame()
+ .createOperatingDay(i, LocalDate.of(2024, 2, 1 + i))
+ )
+ );
+
+ Map operatingDaysPerServiceJourney =
+ DatedServiceJourneysCollector.getOperatingDaysPerServiceJourneyIsAsStrings(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(2, operatingDaysPerServiceJourney.size());
+ assertEquals(
+ 3,
+ operatingDaysPerServiceJourney
+ .get(serviceJourney1.ref())
+ .split(",")
+ .length
+ );
+ assertEquals(
+ 2,
+ operatingDaysPerServiceJourney
+ .get(serviceJourney2.ref())
+ .split(",")
+ .length
+ );
+ }
+
+ private static JAXBValidationContext createContext(
+ NetexEntitiesIndex netexEntitiesIndex
+ ) {
+ return new JAXBValidationContext(
+ "test123",
+ netexEntitiesIndex,
+ null,
+ null,
+ "TST",
+ "fileSchemaVersion",
+ null
+ );
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollectorTest.java b/src/test/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollectorTest.java
new file mode 100644
index 00000000..f2ff7e1e
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/ServiceJourneyDayTypesCollectorTest.java
@@ -0,0 +1,102 @@
+package no.entur.antu.netexdata.collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.IntStream;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.index.api.NetexEntitiesIndex;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.DayOfWeekEnumeration;
+
+class ServiceJourneyDayTypesCollectorTest {
+
+ @Test
+ void testValidServiceJourneysWithDayTypes() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+ NetexEntitiesTestFactory.CreateJourneyPattern journeyPattern =
+ netexEntitiesTestFactory.createJourneyPattern();
+ List stopPointsInJourneyPattern =
+ journeyPattern.createStopPointsInJourneyPattern(4);
+
+ List serviceJourneys =
+ netexEntitiesTestFactory.createServiceJourneys(journeyPattern, 4);
+
+ serviceJourneys.forEach(serviceJourney -> {
+ serviceJourney.createTimetabledPassingTimes(stopPointsInJourneyPattern);
+ serviceJourney.addDayTypeRefs(
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame()
+ .createDayTypes(
+ 4,
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.TUESDAY
+ )
+ );
+ });
+
+ Map dayTypesPerServiceJourneyId =
+ ServiceJourneyDayTypesCollector.getDayTypesPerServiceJourneyIdAsStrings(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(4, dayTypesPerServiceJourneyId.size());
+ dayTypesPerServiceJourneyId.forEach((serviceJourneyId, dayTypes) ->
+ assertEquals(4, dayTypes.split(",").length)
+ );
+ }
+
+ @Test
+ void testInValidServiceJourneysFilteredOut() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+ NetexEntitiesTestFactory.CreateJourneyPattern journeyPattern =
+ netexEntitiesTestFactory.createJourneyPattern();
+ List stopPointsInJourneyPattern =
+ journeyPattern.createStopPointsInJourneyPattern(4);
+
+ List serviceJourneys =
+ netexEntitiesTestFactory.createServiceJourneys(journeyPattern, 4);
+
+ IntStream
+ .range(0, serviceJourneys.size() - 1)
+ .filter(i -> i % 2 == 0)
+ .mapToObj(serviceJourneys::get)
+ .forEach(serviceJourney -> {
+ serviceJourney.createTimetabledPassingTimes(stopPointsInJourneyPattern);
+ serviceJourney.addDayTypeRefs(
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame()
+ .createDayTypes(
+ 4,
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.TUESDAY
+ )
+ );
+ });
+
+ Map dayTypesPerServiceJourneyId =
+ ServiceJourneyDayTypesCollector.getDayTypesPerServiceJourneyIdAsStrings(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(2, dayTypesPerServiceJourneyId.size());
+ }
+
+ private static JAXBValidationContext createContext(
+ NetexEntitiesIndex netexEntitiesIndex
+ ) {
+ return new JAXBValidationContext(
+ "test123",
+ netexEntitiesIndex,
+ null,
+ null,
+ "TST",
+ "fileSchemaVersion",
+ null
+ );
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollectorTest.java b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollectorTest.java
new file mode 100644
index 00000000..c163c8a7
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/ActiveDatesCollectorTest.java
@@ -0,0 +1,286 @@
+package no.entur.antu.netexdata.collectors.activedatecollector;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.List;
+import java.util.Map;
+import no.entur.antu.netexdata.collectors.activedatecollector.calender.ServiceCalendarFrameObject;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.index.api.NetexEntitiesIndex;
+import org.entur.netex.validation.validator.jaxb.JAXBValidationContext;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.DayOfWeekEnumeration;
+
+class ActiveDatesCollectorTest {
+
+ @Test
+ void testParseServiceCalendarFrameInCompositeFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ // Create a composite frame with a service calendar frame
+ netexEntitiesTestFactory
+ .createCompositeFrame()
+ .createServiceCalendarFrame();
+
+ List serviceCalendarFrameObjects =
+ ActiveDatesCollector.parseServiceCalendarFrame(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(1, serviceCalendarFrameObjects.size());
+ }
+
+ @Test
+ void testParseServiceCalendarFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ // Create a service calendar frame
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ List serviceCalendarFrameObjects =
+ ActiveDatesCollector.parseServiceCalendarFrame(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(1, serviceCalendarFrameObjects.size());
+ }
+
+ @Test
+ void testParseMultipleServiceCalendarFrames() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ // Create a service calendar frame
+ netexEntitiesTestFactory.createServiceCalendarFrame(1);
+ netexEntitiesTestFactory.createServiceCalendarFrame(2);
+ netexEntitiesTestFactory.createServiceCalendarFrame(3);
+
+ List serviceCalendarFrameObjects =
+ ActiveDatesCollector.parseServiceCalendarFrame(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(3, serviceCalendarFrameObjects.size());
+ }
+
+ @Test
+ void testParseMultipleServiceCalendarFramesInCompositeFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateCompositeFrame compositeFrame =
+ netexEntitiesTestFactory.createCompositeFrame();
+
+ // Create a service calendar frame
+ compositeFrame.createServiceCalendarFrame(1);
+ compositeFrame.createServiceCalendarFrame(2);
+ compositeFrame.createServiceCalendarFrame(3);
+
+ List serviceCalendarFrameObjects =
+ ActiveDatesCollector.parseServiceCalendarFrame(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(3, serviceCalendarFrameObjects.size());
+ }
+
+ /**
+ * Test that a service calendar frame in a composite frame is returned
+ * when there are both a service calendar frame outside composite frame also exists.
+ * TODO: or do we need to parse both and combine the result?
+ */
+ @Test
+ void testParseServiceCalendarFrameInCompositeFrameIsPrioritized() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ // Create a composite frame with a service calendar frame
+ netexEntitiesTestFactory
+ .createCompositeFrame()
+ .createServiceCalendarFrame(1)
+ .createValidBetween(
+ LocalDateTime.of(2024, 11, 1, 0, 0),
+ LocalDateTime.of(2024, 11, 2, 0, 0)
+ );
+
+ // Create a service calendar frame
+ netexEntitiesTestFactory
+ .createServiceCalendarFrame(2)
+ .createValidBetween(
+ LocalDateTime.of(2024, 12, 1, 0, 0),
+ LocalDateTime.of(2024, 12, 2, 0, 0)
+ );
+
+ List serviceCalendarFrameObjects =
+ ActiveDatesCollector.parseServiceCalendarFrame(
+ createContext(netexEntitiesTestFactory.create())
+ );
+
+ assertEquals(1, serviceCalendarFrameObjects.size());
+ assertEquals(
+ Month.NOVEMBER,
+ serviceCalendarFrameObjects.get(0).validBetween().getFromDate().getMonth()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsAndOperatingDays() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendarFrame
+ .createDayType(1)
+ .withDaysOfWeek(
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType2 =
+ serviceCalendarFrame.createDayType(2);
+ NetexEntitiesTestFactory.CreateDayType dayType3 =
+ serviceCalendarFrame.createDayType(3);
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(1, dayType1)
+ .withOperatingPeriodRef(
+ serviceCalendarFrame.createOperatingPeriod(
+ LocalDate.of(2024, 11, 25),
+ LocalDate.of(2024, 11, 30)
+ )
+ );
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(2, dayType2)
+ .withDate(LocalDate.of(2024, 11, 19));
+
+ NetexEntitiesTestFactory.CreateOperatingDay operatingDay =
+ serviceCalendarFrame.createOperatingDay(LocalDate.of(2024, 11, 23));
+ serviceCalendarFrame
+ .createDayTypeAssignment(3, dayType3)
+ .withOperatingDayRef(operatingDay);
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ Map activeDatesPerId =
+ ActiveDatesCollector.getActiveDatesPerIdAsMapOfStrings(
+ List.of(serviceCalendarFrameObject)
+ );
+
+ assertEquals(
+ "2024-11-25,2024-11-27,2024-11-29",
+ activeDatesPerId.get(dayType1.ref())
+ );
+
+ assertEquals("2024-11-23", activeDatesPerId.get(operatingDay.ref()));
+
+ assertEquals("2024-11-23", activeDatesPerId.get(dayType3.ref()));
+ }
+
+ @Test
+ void testActiveDatesForOperatingDaysOnly() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 25));
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ Map activeDatesPerId =
+ ActiveDatesCollector.getActiveDatesPerIdAsMapOfStrings(
+ List.of(serviceCalendarFrameObject)
+ );
+
+ assertEquals(3, activeDatesPerId.size());
+ assertEquals(
+ "2024-11-25",
+ activeDatesPerId.get(operatingDays.get(0).ref())
+ );
+
+ assertEquals(
+ "2024-11-26",
+ activeDatesPerId.get(operatingDays.get(1).ref())
+ );
+
+ assertEquals(
+ "2024-11-27",
+ activeDatesPerId.get(operatingDays.get(2).ref())
+ );
+ }
+
+ @Test
+ void testActiveDatesWithoutAnyValidityProvided() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 25));
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ Map activeDatesPerId =
+ ActiveDatesCollector.getActiveDatesPerIdAsMapOfStrings(
+ List.of(serviceCalendarFrameObject)
+ );
+
+ assertEquals(3, activeDatesPerId.size());
+ assertEquals(
+ "2024-11-25",
+ activeDatesPerId.get(operatingDays.get(0).ref())
+ );
+
+ assertEquals(
+ "2024-11-26",
+ activeDatesPerId.get(operatingDays.get(1).ref())
+ );
+
+ assertEquals(
+ "2024-11-27",
+ activeDatesPerId.get(operatingDays.get(2).ref())
+ );
+ }
+
+ private static JAXBValidationContext createContext(
+ NetexEntitiesIndex netexEntitiesIndex
+ ) {
+ return new JAXBValidationContext(
+ "test123",
+ netexEntitiesIndex,
+ null,
+ null,
+ "TST",
+ "fileSchemaVersion",
+ null
+ );
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilderTest.java b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilderTest.java
new file mode 100644
index 00000000..684491ec
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ActiveDatesBuilderTest.java
@@ -0,0 +1,1483 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.validation.validator.model.ActiveDates;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.DayOfWeekEnumeration;
+
+class ActiveDatesBuilderTest {
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDates() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 21)
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 2,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingDays() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Dates outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 2,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ @Test
+ void testOperatingDaysAndDatesAreNotEffectedByDayTypes() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 25, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendarFrame
+ .createDayType(1)
+ .withDaysOfWeek(DayOfWeekEnumeration.THURSDAY);
+
+ NetexEntitiesTestFactory.CreateDayType dayType2 = serviceCalendarFrame
+ .createDayType(2)
+ .withDaysOfWeek(DayOfWeekEnumeration.FRIDAY);
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(2, dayType1)
+ .withDate(
+ LocalDate.of(2024, 11, 21) // Thursday
+ );
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(3, dayType2)
+ .withOperatingDayRef(
+ serviceCalendarFrame.createOperatingDay(LocalDate.of(2024, 11, 22)) // Friday
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-21",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType1.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-22",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType2.ref())).toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesDaysAndPeriods() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendarFrame
+ .createDayType(1)
+ .withDaysOfWeek(
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType2 =
+ serviceCalendarFrame.createDayType(2);
+ NetexEntitiesTestFactory.CreateDayType dayType3 =
+ serviceCalendarFrame.createDayType(3);
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(1, dayType1)
+ .withOperatingPeriodRef(
+ serviceCalendarFrame.createOperatingPeriod(
+ LocalDate.of(2024, 11, 25),
+ LocalDate.of(2024, 11, 30)
+ )
+ );
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(2, dayType2)
+ .withDate(LocalDate.of(2024, 11, 19));
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(3, dayType3)
+ .withOperatingDayRef(
+ serviceCalendarFrame.createOperatingDay(LocalDate.of(2024, 11, 23))
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-25,2024-11-27,2024-11-29",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType1.ref())).toString()
+ );
+ assertEquals(
+ "",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType2.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-23",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType3.ref())).toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesDaysAndPeriodsWithDayTypesHavingDays() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendarFrame
+ .createDayType(1)
+ .withDaysOfWeek(
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType2 = serviceCalendarFrame
+ .createDayType(2)
+ .withDaysOfWeek(DayOfWeekEnumeration.SATURDAY);
+ NetexEntitiesTestFactory.CreateDayType dayType3 = serviceCalendarFrame
+ .createDayType(3)
+ .withDaysOfWeek(DayOfWeekEnumeration.SUNDAY);
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(1, dayType1)
+ .withOperatingPeriodRef(
+ serviceCalendarFrame.createOperatingPeriod(
+ LocalDate.of(2024, 11, 25),
+ LocalDate.of(2024, 11, 30)
+ )
+ );
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(2, dayType2)
+ .withDate(LocalDate.of(2024, 11, 23));
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(3, dayType3)
+ .withOperatingDayRef(
+ serviceCalendarFrame.createOperatingDay(LocalDate.of(2024, 11, 24))
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-25,2024-11-27,2024-11-29",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType1.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-23",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType2.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-24",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType3.ref())).toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsOutsideValidPeriod() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 23, 0, 0),
+ LocalDateTime.of(2024, 12, 3, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01,2024-12-02,2024-12-03",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsEveryDay() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 12, 5, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // No Dates are outside the validity of the service calendar frame and
+ // DayTypes are set to 'Every Day' so all the DayTypeAssignments, so all the days are active.
+ assertEquals(
+ "2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsOnWeekDays() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 12, 5, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.WEEKDAYS);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // No Dates are outside the validity of the service calendar frame and
+ // DayTypes are set to 'Week days' so the dates on week ends are filtered out.
+ assertEquals(
+ "2024-11-20,2024-11-21,2024-11-22,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-12-02,2024-12-03,2024-12-04,2024-12-05",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsOnWeekends() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 12, 5, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.WEEKEND);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // No Dates are outside the validity of the service calendar frame and
+ // DayTypes are set to 'Week ends' so the dates on week days are filtered out.
+ assertEquals(
+ "2024-11-23,2024-11-24",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsWithSelectedDates() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 12, 5, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(
+ 3,
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // No Dates are outside the validity of the service calendar frame and
+ // Only the dates on Monday, Wednesday and Friday are included in the active dates, as per day types.
+ assertEquals(
+ "2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-27",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesForDayTypeAssignmentsInServiceCalendar() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Creating Service Calendar inside Service Calendar Frame
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Validity on service calendar
+ serviceCalendar.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendar
+ .createDayType(1)
+ .withDaysOfWeek(
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ NetexEntitiesTestFactory.CreateDayType dayType2 =
+ serviceCalendar.createDayType(2);
+ NetexEntitiesTestFactory.CreateDayType dayType3 =
+ serviceCalendar.createDayType(3);
+
+ serviceCalendar
+ .createDayTypeAssignment(1, dayType1)
+ .withOperatingPeriodRef(
+ serviceCalendar.createOperatingPeriod(
+ LocalDate.of(2024, 11, 25),
+ LocalDate.of(2024, 11, 30)
+ )
+ );
+
+ serviceCalendar
+ .createDayTypeAssignment(2, dayType2)
+ .withDate(LocalDate.of(2024, 11, 19));
+
+ serviceCalendar
+ .createDayTypeAssignment(3, dayType3)
+ .withOperatingDayRef(
+ serviceCalendar.createOperatingDay(LocalDate.of(2024, 11, 23))
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-25,2024-11-27,2024-11-29",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType1.ref())).toString()
+ );
+ assertEquals(
+ "",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType2.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-23",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType3.ref())).toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesFromBothServiceCalendarFrameAndServiceCalendar() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 30, 0, 0)
+ );
+
+ // Creating Service Calendar inside Service Calendar Frame
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Creating data in Service Calendar Frame
+ NetexEntitiesTestFactory.CreateDayType dayType1 = serviceCalendarFrame
+ .createDayType(1)
+ .withDaysOfWeek(
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.WEDNESDAY,
+ DayOfWeekEnumeration.FRIDAY
+ );
+
+ serviceCalendarFrame
+ .createDayTypeAssignment(1, dayType1)
+ .withOperatingPeriodRef(
+ serviceCalendarFrame.createOperatingPeriod(
+ LocalDate.of(2024, 11, 25),
+ LocalDate.of(2024, 11, 30)
+ )
+ );
+
+ // Creating data in service calendar
+ NetexEntitiesTestFactory.CreateDayType dayType2 =
+ serviceCalendar.createDayType(2);
+ NetexEntitiesTestFactory.CreateDayType dayType3 =
+ serviceCalendar.createDayType(3);
+
+ serviceCalendar
+ .createDayTypeAssignment(2, dayType2)
+ .withDate(LocalDate.of(2024, 11, 21));
+
+ serviceCalendar
+ .createDayTypeAssignment(3, dayType3)
+ .withOperatingDayRef(
+ serviceCalendar.createOperatingDay(LocalDate.of(2024, 11, 23))
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-25,2024-11-27,2024-11-29",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType1.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-21",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType2.ref())).toString()
+ );
+ assertEquals(
+ "2024-11-23",
+ dayTypeIdActiveDatesMap.get(new DayTypeId(dayType3.ref())).toString()
+ );
+ }
+
+ @Test
+ void testActiveDatesWithBuildPerOperationDaysInServiceCalendarFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ // Creating 3 Operating days: 2024-11-21, 2024-11-22, 2024-11-23
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ // Build per operating days
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerOperatingDay(serviceCalendarFrameObject);
+
+ assertEquals(
+ "2024-11-21",
+ dayTypeIdActiveDatesMap
+ .get(new OperatingDayId(operatingDays.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-22",
+ dayTypeIdActiveDatesMap
+ .get(new OperatingDayId(operatingDays.get(1).ref()))
+ .toString()
+ );
+ // the third operating day is outside the validity of the service calendar frame
+ assertNull(
+ dayTypeIdActiveDatesMap.get(
+ new OperatingDayId(operatingDays.get(2).ref())
+ )
+ );
+ }
+
+ @Test
+ void testActiveDatesWithBuildPerOperationDaysInServiceCalendar() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Validity on service calendar frame
+ serviceCalendar.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ // Creating 3 Operating days: 2024-11-21, 2024-11-22, 2024-11-23
+ List operatingDays =
+ serviceCalendar.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ // Build per operating days
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerOperatingDay(serviceCalendarFrameObject);
+
+ assertEquals(
+ "2024-11-21",
+ dayTypeIdActiveDatesMap
+ .get(new OperatingDayId(operatingDays.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-22",
+ dayTypeIdActiveDatesMap
+ .get(new OperatingDayId(operatingDays.get(1).ref()))
+ .toString()
+ );
+ // the third operating day is outside the validity of the service calendar frame
+ assertNull(
+ dayTypeIdActiveDatesMap.get(
+ new OperatingDayId(operatingDays.get(2).ref())
+ )
+ );
+ }
+
+ /**
+ * When no validity is set, all the dates should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesWithNoValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 21)
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ assertEquals(
+ 3,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * Validity is set with no from and to dates, all the dates should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesWithEmptyValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 21)
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ assertEquals(
+ 3,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When no validity is set, all the operating days should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingDaysWithNoValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Dates outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 3,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * Validity is set with no from and to dates, all the operating days should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingDaysWithEmptyValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Dates outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 3,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When no validity is set, all the dates in operating period should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsWithNoValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ /**
+ * Validity is set with no from and to dates, all the dates in operating period should be valid.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsWithEmptyValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ /**
+ * When the validity is set only with from date,
+ * all the dates from the from date should be valid.
+ * From date is inclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesWithOnlyFromDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(LocalDateTime.of(2024, 11, 22, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 21)
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ assertEquals(
+ 2,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When the validity is set only with to date,
+ * all the dates till the to date should be valid.
+ * To date is exclusive.
+ * TODO: Should it be inclusive or exclusive? In Chouette it is exclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithDatesWithOnlyToDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withToDate(LocalDateTime.of(2024, 11, 22, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 21)
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ assertEquals(
+ 1,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When the validity is set only with from date,
+ * all the operating days from the from date should be valid.
+ * From date is inclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingDaysWithFromDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(LocalDateTime.of(2024, 11, 22, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Dates outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 2,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When the validity is set only with to date,
+ * all the operating days till the to date should be valid.
+ * To date is exclusive.
+ * TODO: Should it be inclusive or exclusive? In Chouette it is exclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingDaysWithToDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withToDate(LocalDateTime.of(2024, 11, 22, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3);
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(3, LocalDate.of(2024, 11, 21));
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Dates outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ 1,
+ dayTypeIdActiveDatesMap
+ .values()
+ .stream()
+ .filter(ActiveDates::isValid)
+ .count()
+ );
+ }
+
+ /**
+ * When the validity is set only with from date,
+ * all the dates in operating period from the from date should be valid.
+ * From date is inclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsWithFromDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(LocalDateTime.of(2024, 11, 22, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-22,2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+
+ /**
+ * When the validity is set only with to date,
+ * all the dates in operating period till the to date should be valid.
+ * To date is inclusive.
+ * TODO: Should it be inclusive or exclusive? In Chouette it is inclusive.
+ */
+ @Test
+ void testActiveDatesForDayTypeAssignmentsWithOperatingPeriodsWithToDateValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ serviceCalendarFrame.withValidBetween(
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withToDate(LocalDateTime.of(2024, 11, 27, 0, 0))
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(3, DayOfWeekEnumeration.EVERYDAY);
+
+ /*
+ TST:DayType:1 = 2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25
+ TST:DayType:2 = 2024-11-25,2024-11-26,2024-11-27,2024-11-28,2024-11-29,2024-11-30
+ TST:DayType:3 = 2024-11-30,2024-12-01,2024-12-02,2024-12-03,2024-12-04,2024-12-05
+ */
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 3,
+ LocalDate.of(2024, 11, 20),
+ LocalDate.of(2024, 11, 25),
+ 5
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ ActiveDatesBuilder activeDatesBuilder = new ActiveDatesBuilder();
+
+ Map dayTypeIdActiveDatesMap =
+ activeDatesBuilder.buildPerDayType(serviceCalendarFrameObject);
+
+ // Date outside the validity of the service calendar frame is not included in the active dates
+ assertEquals(
+ "2024-11-20,2024-11-21,2024-11-22,2024-11-23,2024-11-24,2024-11-25",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(0).ref()))
+ .toString()
+ );
+ assertEquals(
+ "2024-11-25,2024-11-26,2024-11-27",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(1).ref()))
+ .toString()
+ );
+ assertEquals(
+ "",
+ dayTypeIdActiveDatesMap
+ .get(new DayTypeId(dayTypes.get(2).ref()))
+ .toString()
+ );
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilitiesTest.java b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilitiesTest.java
new file mode 100644
index 00000000..a384464c
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/CalendarUtilitiesTest.java
@@ -0,0 +1,136 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import com.google.common.collect.Multimap;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.function.Function;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.validation.validator.model.DayTypeId;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.Common_VersionFrameStructure;
+import org.rutebanken.netex.model.DayTypeAssignment;
+import org.rutebanken.netex.model.ValidBetween;
+import org.rutebanken.netex.model.ValidityConditions_RelStructure;
+
+class CalendarUtilitiesTest {
+
+ @Test
+ void testToMultimap() {
+ // Arrange
+ NetexEntitiesTestFactory.CreateDayType dayType1 =
+ new NetexEntitiesTestFactory.CreateDayType(1);
+ DayTypeAssignment assignment1 =
+ new NetexEntitiesTestFactory.CreateDayTypeAssignment(1, dayType1)
+ .create();
+ NetexEntitiesTestFactory.CreateDayType dayType2 =
+ new NetexEntitiesTestFactory.CreateDayType(2);
+ DayTypeAssignment assignment2 =
+ new NetexEntitiesTestFactory.CreateDayTypeAssignment(2, dayType2)
+ .create();
+ NetexEntitiesTestFactory.CreateDayType dayType3 =
+ new NetexEntitiesTestFactory.CreateDayType(3);
+ DayTypeAssignment assignment3 =
+ new NetexEntitiesTestFactory.CreateDayTypeAssignment(1, dayType3)
+ .create();
+
+ List assignments = List.of(
+ assignment1,
+ assignment2,
+ assignment3
+ );
+
+ Function keyMapper = DayTypeId::of;
+ Function valueMapper =
+ Function.identity();
+
+ // Act
+ Multimap result = assignments
+ .stream()
+ .collect(CalendarUtilities.toMultimap(keyMapper, valueMapper));
+
+ // Assert
+ assertTrue(result.get(new DayTypeId(dayType1.ref())).contains(assignment1));
+ assertTrue(result.get(new DayTypeId(dayType2.ref())).contains(assignment2));
+ assertTrue(result.get(new DayTypeId(dayType3.ref())).contains(assignment3));
+ }
+
+ @Test
+ void testGetOrDefault() {
+ assertEquals("default", CalendarUtilities.getOrDefault(null, "default"));
+ assertEquals("value", CalendarUtilities.getOrDefault("value", "default"));
+ }
+
+ @Test
+ void testIsWithinValidRange() {
+ ValidBetween validBetween = new ValidBetween()
+ .withFromDate(LocalDateTime.of(2022, 1, 1, 0, 0))
+ .withToDate(LocalDateTime.of(2022, 12, 31, 23, 59));
+
+ LocalDateTime withinRange = LocalDateTime.of(2022, 6, 15, 12, 0);
+ LocalDateTime beforeRange = LocalDateTime.of(2021, 12, 31, 23, 59);
+ LocalDateTime afterRange = LocalDateTime.of(2023, 1, 1, 0, 0);
+
+ assertTrue(CalendarUtilities.isWithinValidRange(withinRange, validBetween));
+ assertFalse(
+ CalendarUtilities.isWithinValidRange(beforeRange, validBetween)
+ );
+ assertFalse(CalendarUtilities.isWithinValidRange(afterRange, validBetween));
+ }
+
+ @Test
+ void testGetValidityForFrameOrDefault() {
+ Common_VersionFrameStructure frameStructure = mock(
+ Common_VersionFrameStructure.class
+ );
+
+ ValidBetween defaultValidity = new ValidBetween()
+ .withFromDate(LocalDateTime.of(2022, 1, 1, 0, 0))
+ .withToDate(LocalDateTime.of(2022, 12, 31, 23, 59));
+
+ when(frameStructure.getValidBetween()).thenReturn(null);
+ when(frameStructure.getContentValidityConditions()).thenReturn(null);
+ when(frameStructure.getValidityConditions()).thenReturn(null);
+
+ ValidBetween result = CalendarUtilities.getValidityForFrameOrDefault(
+ frameStructure,
+ defaultValidity
+ );
+
+ assertEquals(defaultValidity, result);
+ }
+
+ @Test
+ void testGetValidBetween_withEmptyStructure() {
+ ValidityConditions_RelStructure structure = mock(
+ ValidityConditions_RelStructure.class
+ );
+ when(structure.getValidityConditionRefOrValidBetweenOrValidityCondition_())
+ .thenReturn(List.of());
+
+ ValidBetween result = CalendarUtilities.getValidBetween(structure);
+
+ assertNull(result);
+ }
+
+ @Test
+ void testGetValidBetween_withValidCondition() {
+ ValidityConditions_RelStructure structure = mock(
+ ValidityConditions_RelStructure.class
+ );
+ ValidBetween validBetween = new ValidBetween()
+ .withFromDate(LocalDateTime.of(2023, 1, 1, 0, 0))
+ .withToDate(LocalDateTime.of(2023, 12, 31, 23, 59));
+
+ when(structure.getValidityConditionRefOrValidBetweenOrValidityCondition_())
+ .thenReturn(List.of(validBetween));
+
+ ValidBetween result = CalendarUtilities.getValidBetween(structure);
+
+ assertNotNull(result);
+ assertEquals(LocalDateTime.of(2023, 1, 1, 0, 0), result.getFromDate());
+ assertEquals(LocalDateTime.of(2023, 12, 31, 23, 59), result.getToDate());
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObjectTest.java b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObjectTest.java
new file mode 100644
index 00000000..c9e733ea
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ServiceCalendarFrameObjectTest.java
@@ -0,0 +1,627 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.DayOfWeekEnumeration;
+
+class ServiceCalendarFrameObjectTest {
+
+ /**
+ * Test Validity is set on service calendar inside Service calendar frame.
+ */
+ @Test
+ void testValidityOnServiceCalendar() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+ LocalDateTime fromDateTime = LocalDateTime.of(2024, 11, 20, 0, 0);
+ LocalDateTime toDateTime = LocalDateTime.of(2024, 11, 22, 0, 0);
+
+ // Validity on service calendar frame
+ serviceCalendar.createValidBetween(fromDateTime, toDateTime);
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertEquals(
+ fromDateTime,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTime,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Test Validity is set on service calendar frame.
+ */
+ @Test
+ void testValidityOnServiceCalendarFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+ LocalDateTime fromDateTime = LocalDateTime.of(2024, 11, 20, 0, 0);
+ LocalDateTime toDateTime = LocalDateTime.of(2024, 11, 22, 0, 0);
+
+ // Validity on service calendar frame
+ serviceCalendarFrame.createValidBetween(fromDateTime, toDateTime);
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertNull(serviceCalendarFrameObject.serviceCalendar());
+ assertEquals(
+ fromDateTime,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTime,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Validity is set on service calendar frame only.
+ * Service calendar uses validity from service calendar frame.
+ */
+ @Test
+ void testServiceCalendarUsesValidityFromServiceCalendarFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ LocalDateTime fromDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendarFrame.createValidBetween(
+ fromDateTimeServiceCalendarFrame,
+ toDateTimeServiceCalendarFrame
+ );
+
+ // Creating service calendar inside service calendar frame
+ serviceCalendarFrame.createServiceCalendar();
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertNotNull(serviceCalendarFrameObject.serviceCalendar());
+
+ // validity for service calendar frame should be set on service calendar frame object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+
+ // validity for service calendar frame should be set on service calendar object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Validity is set on both service calendar frame and service calendar.
+ * Both uses their own validity.
+ */
+ @Test
+ void testBothServiceCalendarFrameAndServiceCalendarHasValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ LocalDateTime fromDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendarFrame.createValidBetween(
+ fromDateTimeServiceCalendarFrame,
+ toDateTimeServiceCalendarFrame
+ );
+
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Validity on service calendar
+ LocalDateTime fromDateTimeServiceCalendar = LocalDateTime.of(
+ 2024,
+ 12,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendar = LocalDateTime.of(
+ 2024,
+ 12,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendar.createValidBetween(
+ fromDateTimeServiceCalendar,
+ toDateTimeServiceCalendar
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ // validity for service calendar frame should be set on service calendar frame object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+
+ // validity for service calendar should be set on service calendar object
+ assertEquals(
+ fromDateTimeServiceCalendar,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendar,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Validity is set on composite frame, service calendar frame and service calendar.
+ * Both Service calendar frame and service calendar uses their own validity.
+ */
+ @Test
+ void testCompositeFrameAndBothServiceCalendarFrameAndServiceCalendarHasValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ LocalDateTime fromDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 10,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 10,
+ 22,
+ 0,
+ 0
+ );
+ NetexEntitiesTestFactory.CreateValidBetween validBetweenCompositeFrame =
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(fromDateTimeCompositeFrame)
+ .withToDate(toDateTimeCompositeFrame);
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrameInsideCompositeFrame =
+ netexEntitiesTestFactory
+ .createCompositeFrame()
+ .createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ LocalDateTime fromDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendarFrameInsideCompositeFrame.createValidBetween(
+ fromDateTimeServiceCalendarFrame,
+ toDateTimeServiceCalendarFrame
+ );
+
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrameInsideCompositeFrame.createServiceCalendar();
+
+ // Validity on service calendar
+ LocalDateTime fromDateTimeServiceCalendar = LocalDateTime.of(
+ 2024,
+ 12,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendar = LocalDateTime.of(
+ 2024,
+ 12,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendar.createValidBetween(
+ fromDateTimeServiceCalendar,
+ toDateTimeServiceCalendar
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(
+ serviceCalendarFrameInsideCompositeFrame.create(),
+ validBetweenCompositeFrame.create()
+ );
+
+ // validity for service calendar frame should be set on service calendar frame object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+
+ // validity for service calendar should be set on service calendar object
+ assertEquals(
+ fromDateTimeServiceCalendar,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendar,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Validity is set on composite frame only.
+ * Both Service calendar frame and service calendar uses validity from composite frame.
+ */
+ @Test
+ void testDayTypesInServiceCalendarFrameInCompositeFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory
+ .createCompositeFrame()
+ .createServiceCalendarFrame();
+
+ // creating service calendar inside service calendar frame
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Validity on composite frame
+ LocalDateTime fromDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 22,
+ 0,
+ 0
+ );
+ NetexEntitiesTestFactory.CreateValidBetween validBetween =
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(fromDateTimeCompositeFrame)
+ .withToDate(toDateTimeCompositeFrame);
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(
+ serviceCalendarFrame.create(),
+ validBetween.create()
+ );
+
+ // validity for composite frame should be set on service calendar frame object
+ assertEquals(
+ fromDateTimeCompositeFrame,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeCompositeFrame,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+
+ // validity for composite frame should be set on service calendar object
+ assertEquals(
+ fromDateTimeCompositeFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeCompositeFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ /**
+ * Validity is set on composite frame and service calendar frame.
+ * Service calendar should use the Validity from service calendar frame.
+ */
+ @Test
+ void testDayTypesWithServiceCalendarFrameOverridingValidity() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory
+ .createCompositeFrame()
+ .createServiceCalendarFrame();
+
+ // Validity on service calendar frame
+ LocalDateTime fromDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 12,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeServiceCalendarFrame = LocalDateTime.of(
+ 2024,
+ 12,
+ 22,
+ 0,
+ 0
+ );
+ serviceCalendarFrame.createValidBetween(
+ fromDateTimeServiceCalendarFrame,
+ toDateTimeServiceCalendarFrame
+ );
+
+ // creating service calendar inside service calendar frame
+ serviceCalendarFrame.createServiceCalendar();
+
+ // Validity on composite frame
+ LocalDateTime fromDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 20,
+ 0,
+ 0
+ );
+ LocalDateTime toDateTimeCompositeFrame = LocalDateTime.of(
+ 2024,
+ 11,
+ 22,
+ 0,
+ 0
+ );
+ NetexEntitiesTestFactory.CreateValidBetween validBetween =
+ new NetexEntitiesTestFactory.CreateValidBetween(1)
+ .withFromDate(fromDateTimeCompositeFrame)
+ .withToDate(toDateTimeCompositeFrame);
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(
+ serviceCalendarFrame.create(),
+ validBetween.create()
+ );
+
+ // validity for ServiceCalendarFrame should be set on service calendar frame object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.validBetween().getToDate()
+ );
+
+ // validity for ServiceCalendarFrame should be set on service calendar object
+ assertEquals(
+ fromDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getFromDate()
+ );
+ assertEquals(
+ toDateTimeServiceCalendarFrame,
+ serviceCalendarFrameObject.serviceCalendar().validBetween().getToDate()
+ );
+ }
+
+ @Test
+ void testCalendarDataIsCorrectlyParsedInServiceCalenderFrame() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ List dayTypes =
+ serviceCalendarFrame.createDayTypes(
+ 3,
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.TUESDAY
+ );
+
+ List operatingDays =
+ serviceCalendarFrame.createOperatingDays(2, LocalDate.of(2024, 11, 1));
+
+ List operatingPeriods =
+ serviceCalendarFrame.createOperatingPeriods(
+ 4,
+ LocalDate.of(2024, 12, 1),
+ LocalDate.of(2024, 12, 6)
+ );
+
+ serviceCalendarFrame.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 20)
+ );
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+ serviceCalendarFrame.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertEquals(
+ 3,
+ serviceCalendarFrameObject.calendarData().dayTypes().size()
+ );
+ assertEquals(
+ 2,
+ serviceCalendarFrameObject.calendarData().operatingDays().size()
+ );
+ assertEquals(
+ 4,
+ serviceCalendarFrameObject.calendarData().operatingPeriods().size()
+ );
+ assertEquals(
+ 9,
+ serviceCalendarFrameObject.calendarData().dayTypeAssignments().size()
+ );
+ }
+
+ @Test
+ void testCalendarDataIsCorrectlyParsedInServiceCalender() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+ serviceCalendarFrame.createValidBetween(
+ LocalDateTime.of(2024, 11, 20, 0, 0),
+ LocalDateTime.of(2024, 11, 22, 0, 0)
+ );
+
+ NetexEntitiesTestFactory.CreateServiceCalendar serviceCalendar =
+ serviceCalendarFrame.createServiceCalendar();
+
+ List dayTypes =
+ serviceCalendar.createDayTypes(
+ 3,
+ DayOfWeekEnumeration.MONDAY,
+ DayOfWeekEnumeration.TUESDAY
+ );
+
+ List operatingDays =
+ serviceCalendar.createOperatingDays(2, LocalDate.of(2024, 11, 1));
+
+ List operatingPeriods =
+ serviceCalendar.createOperatingPeriods(
+ 4,
+ LocalDate.of(2024, 12, 1),
+ LocalDate.of(2024, 12, 6)
+ );
+
+ serviceCalendar.createDayTypeAssignmentsWithDates(
+ dayTypes,
+ LocalDate.of(2024, 11, 20)
+ );
+ serviceCalendar.createDayTypeAssignmentsWithOperatingDays(
+ dayTypes,
+ operatingDays
+ );
+ serviceCalendar.createDayTypeAssignmentsWithOperatingPeriods(
+ dayTypes,
+ operatingPeriods
+ );
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertEquals(
+ 3,
+ serviceCalendarFrameObject
+ .serviceCalendar()
+ .calendarData()
+ .dayTypes()
+ .size()
+ );
+ assertEquals(
+ 2,
+ serviceCalendarFrameObject
+ .serviceCalendar()
+ .calendarData()
+ .operatingDays()
+ .size()
+ );
+ assertEquals(
+ 4,
+ serviceCalendarFrameObject
+ .serviceCalendar()
+ .calendarData()
+ .operatingPeriods()
+ .size()
+ );
+ assertEquals(
+ 9,
+ serviceCalendarFrameObject
+ .serviceCalendar()
+ .calendarData()
+ .dayTypeAssignments()
+ .size()
+ );
+ }
+
+ @Test
+ void testNoValidityPresent() {
+ NetexEntitiesTestFactory netexEntitiesTestFactory =
+ new NetexEntitiesTestFactory();
+
+ NetexEntitiesTestFactory.CreateServiceCalendarFrame serviceCalendarFrame =
+ netexEntitiesTestFactory.createServiceCalendarFrame();
+
+ ServiceCalendarFrameObject serviceCalendarFrameObject =
+ ServiceCalendarFrameObject.ofNullable(serviceCalendarFrame.create());
+
+ assertNotNull(serviceCalendarFrameObject);
+ }
+}
diff --git a/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriodTest.java b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriodTest.java
new file mode 100644
index 00000000..2a397b9e
--- /dev/null
+++ b/src/test/java/no/entur/antu/netexdata/collectors/activedatecollector/calender/ValidOperatingPeriodTest.java
@@ -0,0 +1,178 @@
+package no.entur.antu.netexdata.collectors.activedatecollector.calender;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import no.entur.antu.netextestdata.NetexEntitiesTestFactory;
+import org.entur.netex.validation.validator.model.OperatingDayId;
+import org.junit.jupiter.api.Test;
+import org.rutebanken.netex.model.OperatingDay;
+import org.rutebanken.netex.model.OperatingPeriod;
+import org.rutebanken.netex.model.ValidBetween;
+
+class ValidOperatingPeriodTest {
+
+ @Test
+ void testValidPeriodWithNoValidBetween() {
+ OperatingPeriod operatingPeriod =
+ new NetexEntitiesTestFactory.CreateOperatingPeriod(
+ 1,
+ LocalDate.of(2023, 1, 1),
+ LocalDate.of(2023, 12, 31)
+ )
+ .create();
+
+ Map operatingDays = Map.of();
+
+ ValidOperatingPeriod result = ValidOperatingPeriod.of(
+ operatingPeriod,
+ null,
+ operatingDays
+ );
+
+ assertEquals(LocalDate.of(2023, 1, 1), result.startDate());
+ assertEquals(LocalDate.of(2023, 12, 31), result.endDate());
+ }
+
+ @Test
+ void testPeriodAdjustedToValidBetween() {
+ OperatingPeriod operatingPeriod =
+ new NetexEntitiesTestFactory.CreateOperatingPeriod(
+ 1,
+ LocalDate.of(2023, 1, 1),
+ LocalDate.of(2023, 12, 31)
+ )
+ .create();
+ ValidBetween validBetween = new ValidBetween()
+ .withFromDate(LocalDateTime.of(2023, 6, 1, 0, 0))
+ .withToDate(LocalDateTime.of(2023, 10, 31, 0, 0));
+
+ Map operatingDays = Map.of();
+
+ ValidOperatingPeriod result = ValidOperatingPeriod.of(
+ operatingPeriod,
+ validBetween,
+ operatingDays
+ );
+
+ assertEquals(LocalDate.of(2023, 6, 1), result.startDate());
+ assertEquals(LocalDate.of(2023, 10, 31), result.endDate());
+ }
+
+ @Test
+ void testPeriodCompletelyOutsideValidBetween() {
+ OperatingPeriod operatingPeriod =
+ new NetexEntitiesTestFactory.CreateOperatingPeriod(
+ 1,
+ LocalDate.of(2023, 1, 1),
+ LocalDate.of(2023, 3, 31)
+ )
+ .create();
+
+ ValidBetween validBetween = new ValidBetween()
+ .withFromDate(LocalDateTime.of(2023, 6, 1, 0, 0))
+ .withToDate(LocalDateTime.of(2023, 10, 31, 0, 0));
+ Map operatingDays = Map.of();
+
+ ValidOperatingPeriod result = ValidOperatingPeriod.of(
+ operatingPeriod,
+ validBetween,
+ operatingDays
+ );
+
+ assertNull(result.startDate());
+ assertNull(result.endDate());
+ }
+
+ @Test
+ void testValidDatesOnWeekDays() {
+ ValidOperatingPeriod period = new ValidOperatingPeriod(
+ LocalDate.of(2023, 6, 1),
+ LocalDate.of(2023, 6, 10)
+ );
+
+ int intDayTypes = 0b0011111; // All weekdays
+
+ List result = period.toDates(Set.of(), intDayTypes);
+
+ List expectedDates = List.of(
+ LocalDate.of(2023, 6, 1),
+ LocalDate.of(2023, 6, 2),
+ LocalDate.of(2023, 6, 5),
+ LocalDate.of(2023, 6, 6),
+ LocalDate.of(2023, 6, 7),
+ LocalDate.of(2023, 6, 8),
+ LocalDate.of(2023, 6, 9)
+ );
+ assertEquals(expectedDates, result);
+ }
+
+ @Test
+ void testValidDatesOnWeekDaysAndExcludedDates() {
+ ValidOperatingPeriod period = new ValidOperatingPeriod(
+ LocalDate.of(2023, 6, 1),
+ LocalDate.of(2023, 6, 10)
+ );
+
+ Set excludedDates = Set.of(LocalDate.of(2023, 6, 5));
+ int intDayTypes = 0b0011111; // All weekdays
+
+ List result = period.toDates(excludedDates, intDayTypes);
+
+ List expectedDates = List.of(
+ LocalDate.of(2023, 6, 1),
+ LocalDate.of(2023, 6, 2),
+ LocalDate.of(2023, 6, 6),
+ LocalDate.of(2023, 6, 7),
+ LocalDate.of(2023, 6, 8),
+ LocalDate.of(2023, 6, 9)
+ );
+ assertEquals(expectedDates, result);
+ }
+
+ @Test
+ void testToDatesOnWeekends() {
+ ValidOperatingPeriod period = new ValidOperatingPeriod(
+ LocalDate.of(2023, 6, 1),
+ LocalDate.of(2023, 6, 10)
+ );
+
+ int intDayTypes = 0b1100000; // Weekend
+
+ List result = period.toDates(null, intDayTypes);
+
+ List expectedDates = List.of(
+ LocalDate.of(2023, 6, 3),
+ LocalDate.of(2023, 6, 4),
+ LocalDate.of(2023, 6, 10)
+ );
+ assertEquals(expectedDates, result);
+ }
+
+ @Test
+ void testToDates_InvalidPeriod() {
+ ValidOperatingPeriod period = new ValidOperatingPeriod(null, null);
+ Set excludedDates = Set.of();
+ int intDayTypes = 0b0111111;
+
+ List result = period.toDates(excludedDates, intDayTypes);
+
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testIsValid() {
+ ValidOperatingPeriod validPeriod = new ValidOperatingPeriod(
+ LocalDate.of(2023, 1, 1),
+ LocalDate.of(2023, 12, 31)
+ );
+ ValidOperatingPeriod invalidPeriod = new ValidOperatingPeriod(null, null);
+
+ assertTrue(validPeriod.isValid());
+ assertFalse(invalidPeriod.isValid());
+ }
+}
diff --git a/src/test/java/no/entur/antu/netextestdata/NetexEntitiesTestFactory.java b/src/test/java/no/entur/antu/netextestdata/NetexEntitiesTestFactory.java
index 28b9b531..d03a8cfc 100644
--- a/src/test/java/no/entur/antu/netextestdata/NetexEntitiesTestFactory.java
+++ b/src/test/java/no/entur/antu/netextestdata/NetexEntitiesTestFactory.java
@@ -8,12 +8,14 @@
import java.math.BigInteger;
import java.time.Duration;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.opengis.gml._3.AbstractRingPropertyType;
import net.opengis.gml._3.DirectPositionListType;
@@ -24,14 +26,11 @@
import net.opengis.gml._3.PolygonType;
import org.entur.netex.index.api.NetexEntitiesIndex;
import org.entur.netex.index.impl.NetexEntitiesIndexImpl;
+import org.entur.netex.validation.test.jaxb.support.JAXBUtils;
import org.rutebanken.netex.model.*;
public class NetexEntitiesTestFactory {
- private static final DayType EVERYDAY = new DayType()
- .withId("EVERYDAY")
- .withName(new MultilingualString().withValue("everyday"));
-
private CreateGenericLine extends Line_VersionStructure> line;
private CreateRoute route;
@@ -50,6 +49,9 @@ public class NetexEntitiesTestFactory {
private final List passengerStopAssignments =
new ArrayList<>();
+ private CreateCompositeFrame compositeFrame;
+ private List serviceCalendarFrames;
+
public NetexEntitiesIndex create() {
NetexEntitiesIndex netexEntitiesIndex = new NetexEntitiesIndexImpl();
@@ -67,6 +69,18 @@ public NetexEntitiesIndex create() {
netexEntitiesIndex.getRouteIndex().put(route.ref(), route.create());
}
+ if (compositeFrame != null) {
+ netexEntitiesIndex.getCompositeFrames().add(compositeFrame.create());
+ }
+
+ if (serviceCalendarFrames != null) {
+ serviceCalendarFrames.forEach(serviceCalendarFrame ->
+ netexEntitiesIndex
+ .getServiceCalendarFrames()
+ .add(serviceCalendarFrame.create())
+ );
+ }
+
fillIndexes(netexEntitiesIndex);
return netexEntitiesIndex;
}
@@ -179,8 +193,7 @@ public CreateLine createLine(int id) {
* @return CreateLine
*/
public CreateLine createLine() {
- line = new CreateLine(1);
- return (CreateLine) line;
+ return createLine(1);
}
/**
@@ -202,8 +215,7 @@ public CreateFlexibleLine createFlexibleLine(int id) {
* @return CreateFlexibleLine
*/
public CreateFlexibleLine createFlexibleLine() {
- line = new CreateFlexibleLine(1);
- return (CreateFlexibleLine) line;
+ return createFlexibleLine(1);
}
/**
@@ -229,12 +241,7 @@ public CreateRoute createRoute(int id) {
* @return CreateRoute
*/
public CreateRoute createRoute() {
- if (line == null) {
- line = new CreateLine(1);
- }
-
- route = new CreateRoute(1, line);
- return route;
+ return createRoute(1);
}
/**
@@ -255,9 +262,7 @@ public CreateJourneyPattern createJourneyPattern(int id) {
* @return CreateJourneyPattern
*/
public CreateJourneyPattern createJourneyPattern() {
- CreateJourneyPattern createJourneyPattern = new CreateJourneyPattern(1);
- journeyPatterns.add(createJourneyPattern);
- return createJourneyPattern;
+ return createJourneyPattern(1);
}
/**
@@ -294,18 +299,30 @@ public CreateServiceJourney createServiceJourney(
*/
public CreateServiceJourney createServiceJourney(
CreateJourneyPattern journeyPattern
+ ) {
+ return createServiceJourney(1, journeyPattern);
+ }
+
+ /**
+ * Adds numberOfServiceJourneys new service journeys with the given journey pattern.
+ * The line will be created if it does not exist, with id 1
+ * The service journeys will have ids from 1 to numberOfServiceJourneys
+ *
+ * @param createJourneyPattern the journey pattern ref for the service journeys
+ * @param numberOfServiceJourneys the number of service journeys to create
+ * @return List of CreateServiceJourney created
+ */
+ public List createServiceJourneys(
+ CreateJourneyPattern createJourneyPattern,
+ int numberOfServiceJourneys
) {
if (line == null) {
line = new CreateLine(1);
}
-
- CreateServiceJourney createServiceJourney = new CreateServiceJourney(
- 1,
- line,
- journeyPattern
- );
- serviceJourneys.add(createServiceJourney);
- return createServiceJourney;
+ return IntStream
+ .rangeClosed(1, numberOfServiceJourneys)
+ .mapToObj(index -> createServiceJourney(index, createJourneyPattern))
+ .toList();
}
/**
@@ -336,12 +353,7 @@ public CreateDeadRun createDeadRun(
* @return CreateDeadRun
*/
public CreateDeadRun createDeadRun(CreateJourneyPattern journeyPattern) {
- if (line == null) {
- line = new CreateLine(1);
- }
- CreateDeadRun deadRun = new CreateDeadRun(1, line, journeyPattern);
- deadRuns.add(deadRun);
- return deadRun;
+ return createDeadRun(1, journeyPattern);
}
/**
@@ -374,36 +386,7 @@ public CreateDatedServiceJourney createDatedServiceJourney(
CreateServiceJourney serviceJourneyRef,
CreateOperatingDay operatingDayRef
) {
- CreateDatedServiceJourney createDatedServiceJourney =
- new CreateDatedServiceJourney(1, serviceJourneyRef, operatingDayRef);
- datedServiceJourneys.add(createDatedServiceJourney);
- return createDatedServiceJourney;
- }
-
- /**
- * Adds numberOfServiceJourneys new service journeys with the given journey pattern.
- * The line will be created if it does not exist, with id 1
- * The service journeys will have ids from 1 to numberOfServiceJourneys
- *
- * @param createJourneyPattern the journey pattern ref for the service journeys
- * @param numberOfServiceJourneys the number of service journeys to create
- * @return List of CreateServiceJourney created
- */
- public List createServiceJourneys(
- CreateJourneyPattern createJourneyPattern,
- int numberOfServiceJourneys
- ) {
- if (line == null) {
- line = new CreateLine(1);
- }
- List createServiceJourneys = IntStream
- .rangeClosed(1, numberOfServiceJourneys)
- .mapToObj(index ->
- new CreateServiceJourney(index, line, createJourneyPattern)
- )
- .toList();
- serviceJourneys.addAll(createServiceJourneys);
- return createServiceJourneys;
+ return createDatedServiceJourney(1, serviceJourneyRef, operatingDayRef);
}
/**
@@ -427,10 +410,7 @@ public CreateServiceJourneyInterchange createServiceJourneyInterchange(
* @return CreateServiceJourneyInterchange
*/
public CreateServiceJourneyInterchange createServiceJourneyInterchange() {
- CreateServiceJourneyInterchange createServiceJourneyInterchange =
- new CreateServiceJourneyInterchange(1);
- interchanges.add(createServiceJourneyInterchange);
- return createServiceJourneyInterchange;
+ return createServiceJourneyInterchange(1);
}
/**
@@ -460,11 +440,11 @@ public CreateServiceLink createServiceLink(
ScheduledStopPointRefStructure fromScheduledStopPointRef,
ScheduledStopPointRefStructure toScheduledStopPointRef
) {
- CreateServiceLink createServiceLink = new CreateServiceLink(1)
- .withFromScheduledStopPointRef(fromScheduledStopPointRef)
- .withToScheduledStopPointRef(toScheduledStopPointRef);
- serviceLinks.add(createServiceLink);
- return createServiceLink;
+ return createServiceLink(
+ 1,
+ fromScheduledStopPointRef,
+ toScheduledStopPointRef
+ );
}
/**
@@ -486,10 +466,7 @@ public CreateFlexibleStopPlace createFlexibleStopPlace(int id) {
* @return CreateFlexibleStopPlace
*/
public CreateFlexibleStopPlace createFlexibleStopPlace() {
- CreateFlexibleStopPlace createFlexibleStopPlace =
- new CreateFlexibleStopPlace(1);
- flexibleStopPlaces.add(createFlexibleStopPlace);
- return createFlexibleStopPlace;
+ return createFlexibleStopPlace(1);
}
/**
@@ -511,29 +488,56 @@ public CreatePassengerStopAssignment createPassengerStopAssignment(int id) {
* @return CreatePassengerStopAssignment
*/
public CreatePassengerStopAssignment createPassengerStopAssignment() {
- CreatePassengerStopAssignment createPassengerStopAssignment =
- new CreatePassengerStopAssignment(1);
- passengerStopAssignments.add(createPassengerStopAssignment);
- return createPassengerStopAssignment;
+ return createPassengerStopAssignment(1);
}
/**
- * Adds a new day type with the given id
+ * Create a new CompositeFrame with the given id
*
- * @param id the id of the day type
- * @return CreateDayType
+ * @param id the id of the CompositeFrame
+ * @return CreateCompositeFrame
*/
- public CreateOperatingDay createOperatingDay(int id, LocalDate date) {
- return new CreateOperatingDay(id, date);
+ public CreateCompositeFrame createCompositeFrame(int id) {
+ compositeFrame = new CreateCompositeFrame(id);
+ return compositeFrame;
}
/**
- * Adds a new day type with id 1
+ * Create a new CompositeFrame with id 1
*
- * @return CreateDayType
+ * @return CreateCompositeFrame
*/
- public CreateOperatingDay createOperatingDay(LocalDate date) {
- return new CreateOperatingDay(1, date);
+ public CreateCompositeFrame createCompositeFrame() {
+ return createCompositeFrame(1);
+ }
+
+ /**
+ * Adds new service calendar frame with the given id
+ * The existing service calendar frame will be overwritten.
+ * The service calendar frame will be added outside the composite frame.
+ *
+ * @param id the id of the service calendar frame
+ * @return CreateServiceCalendarFrame
+ */
+ public CreateServiceCalendarFrame createServiceCalendarFrame(int id) {
+ if (serviceCalendarFrames == null) {
+ serviceCalendarFrames = new ArrayList<>();
+ }
+ CreateServiceCalendarFrame serviceCalendarFrame =
+ new CreateServiceCalendarFrame(id);
+ serviceCalendarFrames.add(serviceCalendarFrame);
+ return serviceCalendarFrame;
+ }
+
+ /**
+ * Adds new service calendar frame with id 1
+ * The existing service calendar frame will be overwritten.
+ * The service calendar frame will be added outside the composite frame.
+ *
+ * @return CreateServiceCalendarFrame
+ */
+ public CreateServiceCalendarFrame createServiceCalendarFrame() {
+ return createServiceCalendarFrame(1);
}
/**
@@ -635,6 +639,570 @@ public final String ref() {
public abstract T create();
}
+ public static class CreateCompositeFrame
+ extends CreateEntity {
+
+ private CreateValidBetween validBetween;
+ private List serviceCalendarFrames;
+
+ public CreateCompositeFrame(int id) {
+ super(id);
+ }
+
+ /**
+ * Adds new service calendar frame with the given id
+ * The existing service calendar frame will be overwritten.
+ * The service calendar frame will be added in the composite frame.
+ *
+ * @param id the id of the service calendar frame
+ * @return CreateServiceCalendarFrame
+ */
+ public CreateServiceCalendarFrame createServiceCalendarFrame(int id) {
+ if (serviceCalendarFrames == null) {
+ serviceCalendarFrames = new ArrayList<>();
+ }
+ CreateServiceCalendarFrame serviceCalendarFrame =
+ new CreateServiceCalendarFrame(id);
+ serviceCalendarFrames.add(serviceCalendarFrame);
+ return serviceCalendarFrame;
+ }
+
+ /**
+ * Adds new service calendar frame with id 1
+ * The existing service calendar frame will be overwritten.
+ * The service calendar frame will be added in the composite frame.
+ *
+ * @return CreateServiceCalendarFrame
+ */
+ public CreateServiceCalendarFrame createServiceCalendarFrame() {
+ return createServiceCalendarFrame(1);
+ }
+
+ public CompositeFrame create() {
+ CompositeFrame compositeFrame = new CompositeFrame().withId(ref());
+
+ if (validBetween != null) {
+ compositeFrame.withValidBetween(validBetween.create());
+ }
+
+ if (serviceCalendarFrames != null) {
+ compositeFrame.setFrames(
+ new Frames_RelStructure()
+ .withCommonFrame(
+ serviceCalendarFrames
+ .stream()
+ .map(CreateServiceCalendarFrame::create)
+ .map(JAXBUtils::createJaxbElement)
+ .collect(Collectors.toCollection(ArrayList::new))
+ )
+ );
+ }
+ return compositeFrame;
+ }
+ }
+
+ public static class CreateValidBetween extends CreateEntity {
+
+ private LocalDateTime fromDateTime;
+ private LocalDateTime localDateTime;
+
+ public CreateValidBetween(int id) {
+ super(id);
+ }
+
+ public CreateValidBetween withFromDate(LocalDateTime fromDateTime) {
+ this.fromDateTime = fromDateTime;
+ return this;
+ }
+
+ public CreateValidBetween withToDate(LocalDateTime localDateTime) {
+ this.localDateTime = localDateTime;
+ return this;
+ }
+
+ @Override
+ public ValidBetween create() {
+ return new ValidBetween()
+ .withFromDate(fromDateTime)
+ .withToDate(localDateTime);
+ }
+ }
+
+ protected abstract static class CreateGenericCalender<
+ T extends EntityStructure
+ >
+ extends CreateEntity