Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Collection;
import java.util.Map;
import org.rutebanken.netex.model.Authority;
import org.rutebanken.netex.model.AvailabilityCondition;
import org.rutebanken.netex.model.Block;
import org.rutebanken.netex.model.Branding;
import org.rutebanken.netex.model.CompositeFrame;
Expand Down Expand Up @@ -297,6 +298,13 @@ public interface NetexEntitiesIndex {
*/
Multimap<String, Parking> getParkingsByParentSiteRefIndex();

/**
* Get a Multimap of AvailabilityConditions by Parking id.
* Populated from {@code validityConditions} on each parsed Parking.
* @return
*/
Multimap<String, AvailabilityCondition> getAvailabilityConditionsByParkingIdIndex();

/**
* Get an entity index of FareZone
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.entur.netex.index.api.NetexEntityIndex;
import org.entur.netex.index.api.VersionedNetexEntityIndex;
import org.rutebanken.netex.model.Authority;
import org.rutebanken.netex.model.AvailabilityCondition;
import org.rutebanken.netex.model.Block;
import org.rutebanken.netex.model.Branding;
import org.rutebanken.netex.model.CompositeFrame;
Expand Down Expand Up @@ -102,6 +103,7 @@ public class NetexEntitiesIndexImpl implements NetexEntitiesIndex {
public final VersionedNetexEntityIndex<FareZone> fareZoneById;
public final VersionedNetexEntityIndex<GroupOfTariffZones> groupOfTariffZonesById;
public final Multimap<String, Parking> parkingsByParentSiteRef;
public final Multimap<String, AvailabilityCondition> availabilityConditionsByParkingId;

// Relations between entities - The Netex XML sometimes rely on the
// nested structure of the XML document, rater than explicit references.
Expand Down Expand Up @@ -179,6 +181,8 @@ public NetexEntitiesIndexImpl() {
this.timetableFrames = new HashSet<>();
this.parkingsByParentSiteRef =
Multimaps.synchronizedListMultimap(ArrayListMultimap.create());
this.availabilityConditionsByParkingId =
Multimaps.synchronizedListMultimap(ArrayListMultimap.create());
}

@Override
Expand Down Expand Up @@ -440,4 +444,9 @@ public void setPublicationTimestamp(LocalDateTime publicationTimestamp) {
public Multimap<String, Parking> getParkingsByParentSiteRefIndex() {
return parkingsByParentSiteRef;
}

@Override
public Multimap<String, AvailabilityCondition> getAvailabilityConditionsByParkingIdIndex() {
return availabilityConditionsByParkingId;
}
}
18 changes: 18 additions & 0 deletions src/main/java/org/entur/netex/loader/parser/SiteFrameParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class SiteFrameParser extends NetexParser<Site_VersionFrameStructure> {
private final Multimap<String, Parking> parkingsByStopPlaceId =
ArrayListMultimap.create();

private final Multimap<String, AvailabilityCondition> availabilityConditionsByParkingId =
ArrayListMultimap.create();

@Override
public void parse(Site_VersionFrameStructure frame) {
if (frame.getStopPlaces() != null) {
Expand Down Expand Up @@ -119,6 +122,9 @@ void setResultOnIndex(NetexEntitiesIndex netexIndex) {
netexIndex.getQuayIndex().putAll(quays.values());
netexIndex.getStopPlaceIdByQuayIdIndex().putAll(stopPlaceIdByQuayId);
netexIndex.getParkingsByParentSiteRefIndex().putAll(parkingsByStopPlaceId);
netexIndex
.getAvailabilityConditionsByParkingIdIndex()
.putAll(availabilityConditionsByParkingId);
netexIndex.getGroupOfTariffZonesIndex().putAll(groupsOfTariffZones);
}

Expand Down Expand Up @@ -166,6 +172,18 @@ private void parseParkings(Collection<Parking> parkingList) {
for (Parking parking : parkingList) {
parkings.add(parking);
parkingsByStopPlaceId.put(parking.getParentSiteRef().getRef(), parking);
if (parking.getValidityConditions() != null) {
for (Object obj : parking
.getValidityConditions()
.getValidityConditionRefOrValidBetweenOrValidityCondition_()) {
if (
obj instanceof JAXBElement<?> jaxb &&
jaxb.getValue() instanceof AvailabilityCondition ac
) {
availabilityConditionsByParkingId.put(parking.getId(), ac);
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.entur.netex;

import java.io.File;
import java.nio.file.Files;
import java.util.Collection;
import org.entur.netex.index.api.NetexEntitiesIndex;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.rutebanken.netex.model.AvailabilityCondition;

class TestParkingAvailabilityConditions {

private static NetexEntitiesIndex index;

@BeforeAll
static void init() {
try {
NetexParser parser = new NetexParser();
File file = new File(
"src/test/resources/ParkingWithAvailabilityConditions.xml"
);
index = parser.parse(Files.newInputStream(file.toPath()));
} catch (Exception e) {
Assertions.fail(e.getMessage(), e);
}
}

@Test
void parkingWithAvailabilityConditionsIsIndexedByParkingId() {
Collection<AvailabilityCondition> conditions = index
.getAvailabilityConditionsByParkingIdIndex()
.get("FSR:Parking:1");
Assertions.assertEquals(2, conditions.size());
}

@Test
void availabilityConditionOpenDayHasCorrectFields() {
AvailabilityCondition openCondition = index
.getAvailabilityConditionsByParkingIdIndex()
.get("FSR:Parking:1")
.stream()
.filter(ac -> "FSR:AvailabilityCondition:1".equals(ac.getId()))
.findFirst()
.orElseThrow();

Assertions.assertTrue(openCondition.isIsAvailable());
Assertions.assertNotNull(openCondition.getDayTypes());
Assertions.assertFalse(
openCondition.getDayTypes().getDayTypeRefOrDayType_().isEmpty()
);
}

@Test
void availabilityConditionClosedDayHasIsAvailableFalse() {
AvailabilityCondition closedCondition = index
.getAvailabilityConditionsByParkingIdIndex()
.get("FSR:Parking:1")
.stream()
.filter(ac -> "FSR:AvailabilityCondition:2".equals(ac.getId()))
.findFirst()
.orElseThrow();

Assertions.assertFalse(closedCondition.isIsAvailable());
}

@Test
void parkingWithoutAvailabilityConditionsReturnsEmptyCollection() {
Collection<AvailabilityCondition> conditions = index
.getAvailabilityConditionsByParkingIdIndex()
.get("FSR:Parking:2");
Assertions.assertTrue(conditions.isEmpty());
}
}
66 changes: 66 additions & 0 deletions src/test/resources/ParkingWithAvailabilityConditions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PublicationDelivery xmlns="http://www.netex.org.uk/netex" xmlns:ns2="http://www.opengis.net/gml/3.2" version="1">
<PublicationTimestamp>2026-07-21T10:00:00</PublicationTimestamp>
<ParticipantRef>FSR</ParticipantRef>
<dataObjects>
<SiteFrame version="1" id="FSR:SiteFrame:1">
<stopPlaces>
<StopPlace version="1" id="FSR:StopPlace:1">
<Name lang="fin">Test Stop</Name>
<Centroid>
<Location>
<Longitude>25.0</Longitude>
<Latitude>60.0</Latitude>
</Location>
</Centroid>
</StopPlace>
</stopPlaces>
<parkings>
<Parking version="1" id="FSR:Parking:1">
<validityConditions>
<AvailabilityCondition version="1" id="FSR:AvailabilityCondition:1">
<IsAvailable>true</IsAvailable>
<dayTypes>
<DayTypeRef ref="FSR:DayType:BusinessDay"/>
</dayTypes>
<timebands>
<Timeband version="1" id="FSR:Timeband:1">
<StartTime>06:00:00</StartTime>
<EndTime>22:00:00</EndTime>
</Timeband>
</timebands>
</AvailabilityCondition>
<AvailabilityCondition version="1" id="FSR:AvailabilityCondition:2">
<IsAvailable>false</IsAvailable>
<dayTypes>
<DayTypeRef ref="FSR:DayType:Sunday"/>
</dayTypes>
</AvailabilityCondition>
</validityConditions>
<Name lang="fin">Test Parking</Name>
<Centroid>
<Location>
<Longitude>25.0</Longitude>
<Latitude>60.0</Latitude>
</Location>
</Centroid>
<ParkingType>urbanParking</ParkingType>
<ParkingLayout>openSpace</ParkingLayout>
<ParentSiteRef ref="FSR:StopPlace:1"/>
</Parking>
<Parking version="1" id="FSR:Parking:2">
<Name lang="fin">Parking without opening hours</Name>
<Centroid>
<Location>
<Longitude>25.1</Longitude>
<Latitude>60.1</Latitude>
</Location>
</Centroid>
<ParkingType>urbanParking</ParkingType>
<ParkingLayout>openSpace</ParkingLayout>
<ParentSiteRef ref="FSR:StopPlace:1"/>
</Parking>
</parkings>
</SiteFrame>
</dataObjects>
</PublicationDelivery>