From 3f7c621d0c32d1a1e7c04d6e9916520becb61cd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 09:01:09 +0000 Subject: [PATCH 001/234] Bump junit from 4.4 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.4 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/commits/r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c05d0888..f0210cf92 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ junit junit - 4.4 + 4.13.1 test From c48856a114032fdfd44804339b36b695e41b18fb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 30 Jan 2024 14:00:22 +0100 Subject: [PATCH 002/234] Adapt to new, de-duplicated location groups --- .../impl/HibernateGtfsRelationalDaoImpl.java | 2 +- .../onebusaway/gtfs/model/LocationGroup.java | 36 +++++++++++-------- .../gtfs/model/LocationGroupElement.java | 13 ++++--- .../GtfsEntitySchemaFactory.java | 1 + .../gtfs/serialization/GtfsReader.java | 12 +++---- .../gtfs/serialization/FlexReaderTest.java | 6 ++++ .../location_group_stops.txt | 31 ++++++++++++++++ .../auburn-transit-flex/location_groups.txt | 33 ++--------------- 8 files changed, 73 insertions(+), 61 deletions(-) create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_group_stops.txt diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 89f7a5881..6ddc0b329 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -295,7 +295,7 @@ public Collection getAllLocationGroupElements() { LocationGroupElement locationGroupElement = new LocationGroupElement(); locationGroupElement.setLocationGroupId(group.getId()); locationGroupElement.setName(group.getName()); - locationGroupElement.setLocation(stopLocation); + locationGroupElement.setStop(stopLocation); return locationGroupElement; })).collect(Collectors.toList()); } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java index 46ef67e98..82e897587 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java @@ -15,18 +15,28 @@ */ package org.onebusaway.gtfs.model; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; +@CsvFields(filename = "location_groups.txt", required = false) public class LocationGroup extends IdentityBean implements StopLocation { private static final long serialVersionUID = 1L; + @CsvField(name = "location_group_id", mapping = DefaultAgencyIdFieldMappingFactory.class) private AgencyAndId id; - private Set locations = new HashSet<>(); - + @CsvField(name = "location_group_name") private String name; + // we use a List, not Set to keep the insertion order. by definition these stops don't have an + // order but it's nice for clients to not randomly change it. + @CsvField(ignore = true) + private List stops = new ArrayList<>(); + @Override public AgencyAndId getId() { return id; @@ -36,18 +46,6 @@ public void setId(AgencyAndId id) { this.id = id; } - public Set getLocations() { - return locations; - } - - private void setLocations(Set locations) { - this.locations = locations; - } - - public void addLocation(StopLocation location) { - this.locations.add(location); - } - public String getName() { return name; } @@ -55,4 +53,12 @@ public String getName() { public void setName(String name) { this.name = name; } + + public void addStop(StopLocation stop) { + stops.add(stop); + } + + public Set getStops() { + return Set.copyOf(stops); + } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java index f12f17c08..4cbfd5293 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java @@ -20,19 +20,18 @@ import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; import org.onebusaway.gtfs.serialization.mappings.StopLocationFieldMappingFactory; -@CsvFields(filename = "location_groups.txt", required = false, prefix = "location_group_") +@CsvFields(filename = "location_groups_stops.txt", required = false, prefix = "location_group_") public class LocationGroupElement extends IdentityBean { private static final long serialVersionUID = 1L; - @CsvField(ignore = true) private int id; @CsvField(name = "location_group_id", mapping = DefaultAgencyIdFieldMappingFactory.class) private AgencyAndId locationGroupId; @CsvField(name = "stop_id", mapping = StopLocationFieldMappingFactory.class) - private StopLocation location; + private StopLocation stop; @CsvField(optional = true) private String name; @@ -54,12 +53,12 @@ public void setLocationGroupId(AgencyAndId locationGroupId) { this.locationGroupId = locationGroupId; } - public StopLocation getLocation() { - return location; + public StopLocation getStop() { + return stop; } - public void setLocation(StopLocation location) { - this.location = location; + public void setStop(StopLocation stop) { + this.stop = stop; } public String getName() { diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java index 8250536fe..f246718a9 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java @@ -47,6 +47,7 @@ public static List> getEntityClasses() { entityClasses.add(Level.class); entityClasses.add(Stop.class); entityClasses.add(StopAreaElement.class); + entityClasses.add(LocationGroup.class); entityClasses.add(LocationGroupElement.class); entityClasses.add(Trip.class); entityClasses.add(Note.class); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index 1168c09f7..963c388c8 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -74,6 +74,7 @@ public GtfsReader() { _entityClasses.add(Level.class); _entityClasses.add(Stop.class); _entityClasses.add(Location.class); + _entityClasses.add(LocationGroup.class); _entityClasses.add(LocationGroupElement.class); _entityClasses.add(Trip.class); _entityClasses.add(StopAreaElement.class); @@ -360,16 +361,13 @@ public void handleEntity(Object entity) { } else if (entity instanceof Location) { Location location = (Location) entity; registerAgencyId(Location.class, location.getId()); + } else if (entity instanceof LocationGroup) { + var group = (LocationGroup) entity; + registerAgencyId(LocationGroup.class, group.getId()); } else if (entity instanceof LocationGroupElement) { LocationGroupElement locationGroupElement = (LocationGroupElement) entity; LocationGroup locationGroup = _entityStore.getEntityForId(LocationGroup.class, locationGroupElement.getLocationGroupId()); - if (locationGroup == null) { - locationGroup = new LocationGroup(); - locationGroup.setId(locationGroupElement.getLocationGroupId()); - locationGroup.setName(locationGroupElement.getName()); - _entityStore.saveEntity(locationGroup); - } - locationGroup.addLocation(locationGroupElement.getLocation()); + locationGroup.addStop(locationGroupElement.getStop()); } else if (entity instanceof StopAreaElement) { var stopAreaElement = (StopAreaElement) entity; var stopArea = _entityStore.getEntityForId(StopArea.class, stopAreaElement.getArea().getId()); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java index 79557807a..dd4566072 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java @@ -105,6 +105,12 @@ public void locationIdAsASeparateColumn() throws CsvEntityIOException, IOExcepti @Test public void locationGroupIdAsSeparateColumn() throws CsvEntityIOException, IOException { var dao = processFeed(GtfsTestData.getAuburnTransitFlex(), AGENCY_ID, false); + var locationGroup = List.copyOf(dao.getAllLocationGroups()).get(0); + assertEquals("Aurburn Loop Stops", locationGroup.getName()); + assertEquals("1_4230479", locationGroup.getId().toString()); + assertEquals(Set.of("1_4230479"), locationGroup.getStops().stream().map(s -> s.getId().toString()).collect( + Collectors.toSet())); + var trip = dao.getAllTrips().stream().filter(t -> t.getId().getId().equals("t_5756013_b_33000_tn_0")).findAny().get(); var stopTimes = dao.getStopTimesForTrip(trip); stopTimes.forEach(st -> assertNotNull(st.getStopLocation())); diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_group_stops.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_group_stops.txt new file mode 100644 index 000000000..a2c6ecce9 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_group_stops.txt @@ -0,0 +1,31 @@ +location_group_id,stop_id +4230479,2583236 +4230479,2583237 +4230479,2583238 +4230479,2583242 +4230479,2583244 +4230479,2583246 +4230479,2583249 +4230479,2583250 +4230479,2583251 +4230479,2583252 +4230479,2583253 +4230479,2583254 +4230479,2583255 +4230479,2583256 +4230479,2583259 +4230479,2583260 +4230479,2583262 +4230479,2583263 +4230479,2583266 +4230479,2583268 +4230479,2583271 +4230479,2583276 +4230479,2583280 +4230479,2583281 +4230479,2583282 +4230479,2583284 +4230479,2583285 +4230479,2751414 +4230479,3446932 +4230479,3446933 diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_groups.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_groups.txt index 7fcf9b6d2..b9eba1b72 100644 --- a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_groups.txt +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/auburn-transit-flex/location_groups.txt @@ -1,31 +1,2 @@ -location_group_id,stop_id,location_group_name -4230479,2583236,Aurburn Loop Stops -4230479,2583237,Aurburn Loop Stops -4230479,2583238,Aurburn Loop Stops -4230479,2583242,Aurburn Loop Stops -4230479,2583244,Aurburn Loop Stops -4230479,2583246,Aurburn Loop Stops -4230479,2583249,Aurburn Loop Stops -4230479,2583250,Aurburn Loop Stops -4230479,2583251,Aurburn Loop Stops -4230479,2583252,Aurburn Loop Stops -4230479,2583253,Aurburn Loop Stops -4230479,2583254,Aurburn Loop Stops -4230479,2583255,Aurburn Loop Stops -4230479,2583256,Aurburn Loop Stops -4230479,2583259,Aurburn Loop Stops -4230479,2583260,Aurburn Loop Stops -4230479,2583262,Aurburn Loop Stops -4230479,2583263,Aurburn Loop Stops -4230479,2583266,Aurburn Loop Stops -4230479,2583268,Aurburn Loop Stops -4230479,2583271,Aurburn Loop Stops -4230479,2583276,Aurburn Loop Stops -4230479,2583280,Aurburn Loop Stops -4230479,2583281,Aurburn Loop Stops -4230479,2583282,Aurburn Loop Stops -4230479,2583284,Aurburn Loop Stops -4230479,2583285,Aurburn Loop Stops -4230479,2751414,Aurburn Loop Stops -4230479,3446932,Aurburn Loop Stops -4230479,3446933,Aurburn Loop Stops +location_group_id,location_group_name +4230479,Aurburn Loop Stops \ No newline at end of file From 2ed2380b82191f93a1769eb6d19e2609390b0bf9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 30 Jan 2024 14:22:22 +0100 Subject: [PATCH 003/234] Use correct file name --- .../java/org/onebusaway/gtfs/model/LocationGroupElement.java | 2 +- .../org/onebusaway/gtfs/serialization/FlexReaderTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java index 4cbfd5293..2e508c8cd 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroupElement.java @@ -20,7 +20,7 @@ import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; import org.onebusaway.gtfs.serialization.mappings.StopLocationFieldMappingFactory; -@CsvFields(filename = "location_groups_stops.txt", required = false, prefix = "location_group_") +@CsvFields(filename = "location_group_stops.txt", required = false, prefix = "location_group_") public class LocationGroupElement extends IdentityBean { private static final long serialVersionUID = 1L; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java index dd4566072..316094cb5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java @@ -108,8 +108,8 @@ public void locationGroupIdAsSeparateColumn() throws CsvEntityIOException, IOExc var locationGroup = List.copyOf(dao.getAllLocationGroups()).get(0); assertEquals("Aurburn Loop Stops", locationGroup.getName()); assertEquals("1_4230479", locationGroup.getId().toString()); - assertEquals(Set.of("1_4230479"), locationGroup.getStops().stream().map(s -> s.getId().toString()).collect( - Collectors.toSet())); + var actualStops = locationGroup.getStops().stream().map(s -> s.getId().toString()).collect(Collectors.toList()); + assertEquals(30, actualStops.size()); var trip = dao.getAllTrips().stream().filter(t -> t.getId().getId().equals("t_5756013_b_33000_tn_0")).findAny().get(); var stopTimes = dao.getStopTimesForTrip(trip); From 5039d899d597eb2b25be4b8f08101d7addf40e59 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 30 Jan 2024 14:27:46 +0100 Subject: [PATCH 004/234] Fix rename --- .../onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 6ddc0b329..0b9fac66e 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -291,7 +291,7 @@ public Collection getAllAreas() { @Override public Collection getAllLocationGroupElements() { Collection groups = _ops.find("FROM LocationGroup"); - return groups.stream().flatMap(group -> group.getLocations().stream().map(stopLocation -> { + return groups.stream().flatMap(group -> group.getStops().stream().map(stopLocation -> { LocationGroupElement locationGroupElement = new LocationGroupElement(); locationGroupElement.setLocationGroupId(group.getId()); locationGroupElement.setName(group.getName()); From 266852aca5b05b68c2a994ee4d3f9e35f1415ce2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 30 Jan 2024 14:36:34 +0100 Subject: [PATCH 005/234] Make names conform to hibernate expectations --- .../gtfs/impl/HibernateGtfsRelationalDaoImpl.java | 2 +- .../java/org/onebusaway/gtfs/model/LocationGroup.java | 8 ++++++-- .../org/onebusaway/gtfs/serialization/GtfsReader.java | 2 +- .../org/onebusaway/gtfs/serialization/FlexReaderTest.java | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 0b9fac66e..6ddc0b329 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -291,7 +291,7 @@ public Collection getAllAreas() { @Override public Collection getAllLocationGroupElements() { Collection groups = _ops.find("FROM LocationGroup"); - return groups.stream().flatMap(group -> group.getStops().stream().map(stopLocation -> { + return groups.stream().flatMap(group -> group.getLocations().stream().map(stopLocation -> { LocationGroupElement locationGroupElement = new LocationGroupElement(); locationGroupElement.setLocationGroupId(group.getId()); locationGroupElement.setName(group.getName()); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java index 82e897587..b7c884dcb 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java @@ -16,6 +16,7 @@ package org.onebusaway.gtfs.model; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Set; import org.onebusaway.csv_entities.schema.annotations.CsvField; @@ -54,11 +55,14 @@ public void setName(String name) { this.name = name; } - public void addStop(StopLocation stop) { + public void addLocation(StopLocation stop) { stops.add(stop); } + public void setLocations(Collection stop) { + stops.addAll(stop); + } - public Set getStops() { + public Set getLocations() { return Set.copyOf(stops); } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index 963c388c8..2cb17d277 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -367,7 +367,7 @@ public void handleEntity(Object entity) { } else if (entity instanceof LocationGroupElement) { LocationGroupElement locationGroupElement = (LocationGroupElement) entity; LocationGroup locationGroup = _entityStore.getEntityForId(LocationGroup.class, locationGroupElement.getLocationGroupId()); - locationGroup.addStop(locationGroupElement.getStop()); + locationGroup.addLocation(locationGroupElement.getStop()); } else if (entity instanceof StopAreaElement) { var stopAreaElement = (StopAreaElement) entity; var stopArea = _entityStore.getEntityForId(StopArea.class, stopAreaElement.getArea().getId()); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java index 316094cb5..880377d60 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java @@ -108,7 +108,7 @@ public void locationGroupIdAsSeparateColumn() throws CsvEntityIOException, IOExc var locationGroup = List.copyOf(dao.getAllLocationGroups()).get(0); assertEquals("Aurburn Loop Stops", locationGroup.getName()); assertEquals("1_4230479", locationGroup.getId().toString()); - var actualStops = locationGroup.getStops().stream().map(s -> s.getId().toString()).collect(Collectors.toList()); + var actualStops = locationGroup.getLocations().stream().map(s -> s.getId().toString()).collect(Collectors.toList()); assertEquals(30, actualStops.size()); var trip = dao.getAllTrips().stream().filter(t -> t.getId().getId().equals("t_5756013_b_33000_tn_0")).findAny().get(); From 592f1f25664d99ebce56ee3d753103e93aee5bfa Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Thu, 1 Feb 2024 07:52:17 -0500 Subject: [PATCH 006/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-1.4.15 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index a638ba00d..4ea25286a 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index a4739711e..cc41b4d21 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index cfdc003e1..f61db74ec 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.15-SNAPSHOT + 1.4.15 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 44616a060..d54fff12f 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.15-SNAPSHOT + 1.4.15 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 670b96cff..11d15c912 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 531b3f39b..b5f9af995 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 4d44bba37..e521acb8c 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 org.onebusaway onebusaway-gtfs - 1.4.15-SNAPSHOT + 1.4.15 org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 1d5cd7220..9f2398d59 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 diff --git a/pom.xml b/pom.xml index fc0d06565..cf9192e8c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.15-SNAPSHOT + 1.4.15 pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-1.4.15 From fd8e842cca98680df6efeb9675a8f76ebd1b5a89 Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Thu, 1 Feb 2024 07:52:29 -0500 Subject: [PATCH 007/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 4ea25286a..fba79c0f1 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index cc41b4d21..e124a4350 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index f61db74ec..4c0f4e920 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.15 + 1.4.16-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index d54fff12f..31f7d25eb 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.15 + 1.4.16-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 11d15c912..1aec10c6e 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index b5f9af995..6cbcac0d8 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index e521acb8c..dfebe0058 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT org.onebusaway onebusaway-gtfs - 1.4.15 + 1.4.16-SNAPSHOT org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 9f2398d59..6da432381 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT diff --git a/pom.xml b/pom.xml index cf9192e8c..a22ecf61d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.15 + 1.4.16-SNAPSHOT pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-1.4.15 + HEAD From 1829ba6e8c1b025c4c8b755414e50b2047c33cff Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 15:15:15 +0200 Subject: [PATCH 008/234] Remove backwards-compatibility with old versions of flex --- .../org/onebusaway/gtfs/model/StopTime.java | 90 +------------------ .../model/StopTimeWithUnderscoreTest.java | 4 +- .../FlexDropOffSpellingTest.java | 5 -- 3 files changed, 4 insertions(+), 95 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java index 7e9bb9a72..b80f961d3 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java @@ -61,44 +61,12 @@ public final class StopTime extends IdentityBean implements @CsvField(optional = true, mapping = StopTimeFieldMappingFactory.class) private int departureTime = MISSING_VALUE; - /** - * @deprecated - * GTFS-Flex v2.1 renamed this field. Use {@link #startPickupDropOffWindow} instead. - */ - @Deprecated - @CsvField(optional = true, mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") - private int minArrivalTime = MISSING_VALUE; - @CsvField(optional = true, name = "start_pickup_drop_off_window", mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") private int startPickupDropOffWindow = MISSING_VALUE; - /** - * @deprecated - * GTFS-Flex v2.1 renamed "dropoff" to "drop off": https://github.com/MobilityData/gtfs-flex/commit/547200dfb580771265ae14b07d9bfd7b91c16ed2 - */ - @Deprecated - @CsvField(optional = true, name = "start_pickup_dropoff_window", mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") - public int oldSpellingOfStartPickupDropOffWindow = MISSING_VALUE; - - /** - * @deprecated - * GTFS-Flex v2.1 renamed this field. Use {@link #endPickupDropOffWindow} instead. - */ - @Deprecated - @CsvField(optional = true, mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") - private int maxDepartureTime = MISSING_VALUE; - @CsvField(optional = true, name = "end_pickup_drop_off_window", mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") private int endPickupDropOffWindow = MISSING_VALUE; - /** - * @deprecated - * GTFS-Flex v2.1 renamed "dropoff" to "drop off": https://github.com/MobilityData/gtfs-flex/commit/547200dfb580771265ae14b07d9bfd7b91c16ed2 - */ - @Deprecated - @CsvField(optional = true, name = "end_pickup_dropoff_window", mapping = StopTimeFieldMappingFactory.class, defaultValue = "-999") - public int oldSpellingOfEndPickupDropOffWindow = MISSING_VALUE; - @CsvField(optional = true, defaultValue = "-999") private int timepoint = MISSING_VALUE; @@ -192,9 +160,7 @@ public StopTime(StopTime st) { this.dropOffType = st.dropOffType; this.id = st.id; this.pickupType = st.pickupType; - this.minArrivalTime = st.minArrivalTime; this.startPickupDropOffWindow = st.startPickupDropOffWindow; - this.maxDepartureTime = st.maxDepartureTime; this.endPickupDropOffWindow = st.endPickupDropOffWindow; this.continuousPickup = st.continuousPickup; this.continuousDropOff = st.continuousDropOff; @@ -411,50 +377,17 @@ public void clearDepartureTime() { this.departureTime = MISSING_VALUE; } - @Deprecated - public int getMinArrivalTime() { - return minArrivalTime; - } - - @Deprecated - public void setMinArrivalTime(int minArrivalTime) { - this.minArrivalTime = minArrivalTime; - } public int getStartPickupDropOffWindow() { - if (startPickupDropOffWindow != MISSING_VALUE) { - return startPickupDropOffWindow; - } else if(oldSpellingOfStartPickupDropOffWindow != MISSING_VALUE){ - return oldSpellingOfStartPickupDropOffWindow; - } else { - return minArrivalTime; - } + return startPickupDropOffWindow; } public void setStartPickupDropOffWindow(int startPickupDropOffWindow) { this.startPickupDropOffWindow = startPickupDropOffWindow; } - @Deprecated - public int getMaxDepartureTime() { - return maxDepartureTime; - } - - @Deprecated - public void setMaxDepartureTime(int maxDepartureTime) { - this.maxDepartureTime = maxDepartureTime; - } - public int getEndPickupDropOffWindow() { - if (endPickupDropOffWindow != MISSING_VALUE) { - return endPickupDropOffWindow; - } - else if (oldSpellingOfEndPickupDropOffWindow != MISSING_VALUE) { - return oldSpellingOfEndPickupDropOffWindow; - } - else { - return maxDepartureTime; - } + return endPickupDropOffWindow; } public void setEndPickupDropOffWindow(int endPickupDropOffWindow) { @@ -806,30 +739,11 @@ public void setFreeRunningFlag(String freeRunningFlag) { } this.freeRunningFlag = freeRunningFlag; } - @Deprecated - public void setOldSpellingOfStartPickupDropOffWindow(int time) { - oldDropOffSpellingWarning("start"); - this.oldSpellingOfStartPickupDropOffWindow = time; - } - - @Deprecated - public void setOldSpellingOfEndPickupDropOffWindow(int time) { - oldDropOffSpellingWarning("end"); - this.oldSpellingOfEndPickupDropOffWindow = time; - } private static void oldDropOffSpellingWarning(String type) { _log.warn("This feed uses the old spelling of '{}_pickup_drop_off_window' ('dropoff' instead of 'drop_off'). " + "Compatibility will be removed in the future, so please update your feed to be in line with the latest Flex V2 spec:" + " https://github.com/MobilityData/gtfs-flex/commit/547200dfb", type); } - @Deprecated - public int getOldSpellingOfStartPickupDropOffWindow() { - return this.oldSpellingOfStartPickupDropOffWindow; - } - @Deprecated - public int getOldSpellingOfEndPickupDropOffWindow() { - return oldSpellingOfEndPickupDropOffWindow; - } } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java index 9a5240a7e..3a4396b58 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java @@ -80,7 +80,7 @@ public void testWithoutUnderscore() throws IOException { _gtfs.putDefaultTrips(); _gtfs.putDefaultStops(); _gtfs.putLines("stop_times.txt", - "trip_id,stop_id,stop_sequence,arrival_time,departure_time,end_pickup_dropoff_window", + "trip_id,stop_id,stop_sequence,arrival_time,departure_time,end_pickup_drop_off_window", "T10-0,100,0,05:55:55,08:00:00,08:23:23", "T10-0,200,1,05:55:55,09:00:00,08:44:44"); GtfsRelationalDao dao = _gtfs.read(); @@ -94,7 +94,7 @@ public void testWithoutUnderscore() throws IOException { boolean foundUnderscoreParam = false; while(scan.hasNext()){ String line = scan.nextLine(); - if(line.contains("end_pickup_dropoff_window")){ + if(line.contains("end_pickup_drop_off_window")){ foundUnderscoreParam = true; } } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java index d5bb63ad7..d17a70fa5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java @@ -39,11 +39,6 @@ */ public class FlexDropOffSpellingTest { - @Test - public void oldSpelling() throws IOException { - testFlexStopTimeWithSpelling("dropoff"); - } - @Test public void newSpelling() throws IOException { testFlexStopTimeWithSpelling("drop_off"); From b82e526f2a88b94346c46fdcf8fba3cac6f7ceed Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 15:20:03 +0200 Subject: [PATCH 009/234] Update Javadoc --- .../onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java index d17a70fa5..8848fc886 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java @@ -35,7 +35,7 @@ * * Since it's hard to spot: the change is in the word "dropoff" vs "drop_off". * - * This test makes sure that both spellings are understood. + * This test makes sure that the new spelling is understood. */ public class FlexDropOffSpellingTest { From 6f4b780336a0e857d4da98087d88e9d591f3c22e Mon Sep 17 00:00:00 2001 From: caylasavitzky Date: Tue, 9 Apr 2024 14:40:59 -0400 Subject: [PATCH 010/234] allowing non-numerical route&stop-id values in alternate_stop_names_exceptions.txt --- .../gtfs/model/AlternateStopNameException.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/AlternateStopNameException.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/AlternateStopNameException.java index 4dd04882f..6a57c4ed6 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/AlternateStopNameException.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/AlternateStopNameException.java @@ -26,13 +26,13 @@ public class AlternateStopNameException extends IdentityBean { private int id; @CsvField(optional = true) - int routeId; + String routeId; @CsvField(optional = true) int directionId; @CsvField(optional = true) - int stopId; + String stopId; @CsvField(optional = true) String alternateStopName; @@ -58,11 +58,11 @@ public void setId(Integer id) { this.id = id; } - public int getRouteId() { + public String getRouteId() { return routeId; } - public void setRouteId(int routeId) { + public void setRouteId(String routeId) { this.routeId = routeId; } @@ -74,11 +74,11 @@ public void setDirectionId(int directionId) { this.directionId = directionId; } - public int getStopId() { + public String getStopId() { return stopId; } - public void setStopId(int stopId) { + public void setStopId(String stopId) { this.stopId = stopId; } From 7837f87c6908d11f7638e1d697b9fd775679b8ff Mon Sep 17 00:00:00 2001 From: caylasavitzky Date: Wed, 10 Apr 2024 11:21:34 -0400 Subject: [PATCH 011/234] location_group_name is an optional field --- .../src/main/java/org/onebusaway/gtfs/model/LocationGroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java index b7c884dcb..2fe154b51 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/LocationGroup.java @@ -30,7 +30,7 @@ public class LocationGroup extends IdentityBean implements StopLoca @CsvField(name = "location_group_id", mapping = DefaultAgencyIdFieldMappingFactory.class) private AgencyAndId id; - @CsvField(name = "location_group_name") + @CsvField(name = "location_group_name", optional = true) private String name; // we use a List, not Set to keep the insertion order. by definition these stops don't have an From 40a03b32d7350bf358b4a7c3668e3bc0acd784e7 Mon Sep 17 00:00:00 2001 From: caylasavitzky Date: Thu, 11 Apr 2024 12:36:42 -0400 Subject: [PATCH 012/234] adding test for propogating stop_id, location_id, and location_group_id --- .../onebusaway/gtfs_merge/GtfsMergerTest.java | 112 +++++++++++++++++- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java index 5beb27339..aafe73725 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java @@ -21,17 +21,14 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; -import org.onebusaway.gtfs.model.Agency; -import org.onebusaway.gtfs.model.Route; -import org.onebusaway.gtfs.model.ServiceCalendar; -import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.Trip; +import org.onebusaway.gtfs.model.*; import org.onebusaway.gtfs.serialization.GtfsReader; import org.onebusaway.gtfs.services.GtfsRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; @@ -358,6 +355,111 @@ public void testRenameStrategy() throws IOException { assertTrue("expect a puget stop", pugetStopFound); } +// tests stop, location, and location group + @Test + public void testStopTimeProxies() throws IOException { + // lowest priority feed (first) to highest priority feed (last) + _oldGtfs.putLines("agency.txt", "agency_id,agency_name,agency_url,agency_timezone", + "3,Pierce,http://p.us/,America/Los_Angeles"); + _oldGtfs.putLines("routes.txt", + "route_id,route_short_name,route_long_name,route_type", + "R10,10,The Pierce Ten,3"); + _oldGtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon", + "100,The Stop,47.654403,-122.305211", + "200,Pierce Other Stop,47.668594,-122.298859", + "400,Pierce Only Stop,47.669563,-122.305420"); + _oldGtfs.putLines( + "calendars.txt", + "service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date", + "sid0,1,1,1,1,1,0,0,20110101,20111231"); + _oldGtfs.putLines("trips.txt", "route_id,service_id,trip_id", + "R10,sid0,T10-0", + "R10,sid0,T10-1"); + _oldGtfs.putStopTimes("T10-0", "100,200"); // stop conflict only + _oldGtfs.putStopTimes("T10-1", "100,400"); + _oldGtfs.putLines("location_groups.txt","location_group_id","3", + "33"); + _oldGtfs.putLines("stop_times.txt", + "trip_id,stop_id,stop_sequence,arrival_time,departure_time,location_group_id", + "T10-0,100,0,08:00:00,08:00:00,3", + "T10-0,200,1,09:00:00,09:00:00,33", + "T10-1,100,1,08:00:00,08:00:00,", + "T10-1,400,1,09:00:00,09:00:00,"); + + + _newGtfs.putLines("agency.txt", "agency_id,agency_name,agency_url,agency_timezone", + "1,Metro,http://metro.gov/,America/Los_Angeles", + "3,Pierce,http://p.us/,America/Los_Angeles"); + _newGtfs.putLines("routes.txt", + "agency_id,route_id,route_short_name,route_long_name,route_type", + "1,R10,10,The KCM Ten,3"); + _newGtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon", + "100,The Stop,47.654403,-122.305211", + "200,The Other Stop,47.656303,-122.315436", + "300,The Third Stop,47.668575,-122.283653"); + _newGtfs.putCalendars(1, "mask=1111100", "start_date=20120504", + "end_date=20120608"); + _newGtfs.putLines("trips.txt", "route_id,service_id,trip_id", "R10,sid0,T10-0"); + _newGtfs.putLines("stop_times.txt", + "trip_id,stop_id,stop_sequence,arrival_time,departure_time", + "T10-0,100,0,08:00:00,08:00:00", + "T10-0,200,1,09:00:00,09:00:00", + "T10-0,300,1,10:00:00,10:00:00"); + + _pugetGtfs = MockGtfs.create(); + _pugetGtfs.putLines("agency.txt", "agency_id,agency_name,agency_url,agency_timezone", + "0,Puget Sound Region,http://puget-sound.gov/,America/Los_Angeles"); + _pugetGtfs.putLines("routes.txt", + "route_id,route_short_name,route_long_name,route_type", + "r0,,,3"); + _pugetGtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon",""); + _pugetGtfs.putLines( + "calendars.txt", + "service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date", + "sid0,1,1,1,1,1,0,0,20110101,20111231"); + _pugetGtfs.putCalendars(1, "mask=1111100", "start_date=20120504", + "end_date=20120608"); + _pugetGtfs.putLines("trips.txt", "route_id,service_id,trip_id", + "r0,sid0,t0"); + _pugetGtfs.putLines("locations.geojson", + "{ \"type\": \"FeatureCollection\",", + " \"features\": [", + " { \"type\": \"Feature\",", + " \"id\":\"s0\",", + " \"geometry\": {", + " \"type\": \"Polygon\",", + " \"coordinates\": [", + " [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],", + " [100.0, 1.0], [100.0, 0.0] ]", + " ]", + "", + " },", + " \"properties\": {", + " }", + " }", + " ]", + " }"); + _pugetGtfs.putLines("stop_times.txt", + "trip_id,stop_id,stop_sequence,arrival_time,departure_time", + "t0,s0,0,01:00:00,01:00:03"); + + + + GtfsRelationalDao dao = merge(); + + // make sure all stoptimes only have stop_id, location_id, or location_group_id + Iterator itt = dao.getAllStopTimes().iterator(); + while(itt.hasNext()){ + StopTime st = itt.next(); + boolean hasST = st.getStop()!=null; + boolean hasLoc = st.getLocation()!=null; + boolean hasLocGroup = st.getLocationGroup()!=null; + assertTrue("multiple ids found for stop: "+st.getStop()+ + ", location_id: "+st.getLocation()+ + ", location_id: "+st.getLocationGroup(), + !(hasST & hasLoc | hasST && hasLocGroup | hasLoc & hasLocGroup)); + } + } private GtfsRelationalDao merge() throws IOException { List paths = new ArrayList(); From ea7bcaae1cb126c4cdd0367c228896a3ff4726b3 Mon Sep 17 00:00:00 2001 From: caylasavitzky Date: Thu, 11 Apr 2024 12:39:59 -0400 Subject: [PATCH 013/234] preventing stop_id from propogating to location_id and location_group_id during merge --- .../src/main/java/org/onebusaway/gtfs/model/StopTime.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java index 7e9bb9a72..837e02597 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java @@ -280,7 +280,7 @@ public void setToStopSequence(Integer toStopSequence) { @Override public StopLocation getStop() { - if (proxy != null) { + if (proxy != null & stop!=null) { return proxy.getStop(); } return stop; @@ -288,7 +288,7 @@ public StopLocation getStop() { @Override public StopLocation getLocation() { - if (proxy != null) { + if (proxy != null & location!=null) { return proxy.getLocation(); } return location; @@ -296,7 +296,7 @@ public StopLocation getLocation() { @Override public StopLocation getLocationGroup() { - if (proxy != null) { + if (proxy != null & locationGroup!=null) { return proxy.getLocationGroup(); } return locationGroup; From aab90c05315577af8685ec38534858a60297968a Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Wed, 17 Apr 2024 10:09:52 -0400 Subject: [PATCH 014/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-1.4.16 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index fba79c0f1..e5e4359a4 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index e124a4350..b799b542d 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 4c0f4e920..8bd21983e 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.16-SNAPSHOT + 1.4.16 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 31f7d25eb..79d83a600 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.16-SNAPSHOT + 1.4.16 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 1aec10c6e..6bcdc57e4 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 6cbcac0d8..f5a32674d 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index dfebe0058..c3ef5fb5f 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 org.onebusaway onebusaway-gtfs - 1.4.16-SNAPSHOT + 1.4.16 org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 6da432381..c4d341f84 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 diff --git a/pom.xml b/pom.xml index a22ecf61d..8e78ccac8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.16-SNAPSHOT + 1.4.16 pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-1.4.16 From 876c386a34dfe2601342df6105524787570f1e3e Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Wed, 17 Apr 2024 10:09:54 -0400 Subject: [PATCH 015/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index e5e4359a4..a6270a4cb 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index b799b542d..4b91806bc 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 8bd21983e..ed1baabe1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.16 + 1.4.17-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 79d83a600..3383831fb 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.16 + 1.4.17-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 6bcdc57e4..f67feb7bd 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index f5a32674d..884e6d20a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index c3ef5fb5f..f1aa3fc64 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT org.onebusaway onebusaway-gtfs - 1.4.16 + 1.4.17-SNAPSHOT org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index c4d341f84..9e8d10249 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT diff --git a/pom.xml b/pom.xml index 8e78ccac8..b49b04cb0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.16 + 1.4.17-SNAPSHOT pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-1.4.16 + HEAD From ab4577ab7e088d2096f162079a249368d34bd837 Mon Sep 17 00:00:00 2001 From: caylasavitzky Date: Wed, 1 May 2024 12:58:29 -0400 Subject: [PATCH 016/234] preventing StopLocations from propogating to one another during merge --- .../onebusaway/gtfs/impl/StopTimeArray.java | 23 +++++++++++-------- .../org/onebusaway/gtfs/model/StopTime.java | 20 ++++++++-------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/StopTimeArray.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/StopTimeArray.java index a2a73f1be..1561e0ff7 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/StopTimeArray.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/StopTimeArray.java @@ -21,12 +21,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; -import org.onebusaway.gtfs.model.Area; -import org.onebusaway.gtfs.model.BookingRule; -import org.onebusaway.gtfs.model.StopLocation; -import org.onebusaway.gtfs.model.StopTime; -import org.onebusaway.gtfs.model.StopTimeProxy; -import org.onebusaway.gtfs.model.Trip; +import org.onebusaway.gtfs.model.*; public class StopTimeArray extends AbstractList { @@ -36,6 +31,10 @@ public class StopTimeArray extends AbstractList { private StopLocation[] stops = new StopLocation[0]; + private StopLocation[] locations = new StopLocation[0]; + + private StopLocation[] locationGroups = new StopLocation[0]; + private Area[] startServiceAreas = new Area[0]; private Area[] endServiceAreas = new Area[0]; @@ -89,6 +88,8 @@ public boolean add(StopTime stopTime) { startServiceAreas[index] = stopTime.getStartServiceArea(); endServiceAreas[index] = stopTime.getEndServiceArea(); stops[index] = stopTime.getStop(); + locations[index] = stopTime.getLocation(); + locationGroups[index] = stopTime.getLocationGroup(); arrivalTimes[index] = stopTime.getArrivalTime(); departureTimes[index] = stopTime.getDepartureTime(); timepoints[index] = stopTime.getTimepoint(); @@ -151,6 +152,8 @@ private void setLength(int newLength) { this.startServiceAreas = Arrays.copyOf(this.startServiceAreas, newLength); this.endServiceAreas = Arrays.copyOf(this.endServiceAreas, newLength); this.stops = Arrays.copyOf(this.stops, newLength); + this.locationGroups = Arrays.copyOf(this.locationGroups,newLength); + this.locations = Arrays.copyOf(this.locations,newLength); this.arrivalTimes = Arrays.copyOf(this.arrivalTimes, newLength); this.departureTimes = Arrays.copyOf(this.departureTimes, newLength); this.timepoints = Arrays.copyOf(this.timepoints, newLength); @@ -260,12 +263,12 @@ public StopLocation getStop() { @Override public StopLocation getLocation() { - return stops[index]; + return locations[index]; } @Override public StopLocation getLocationGroup() { - return stops[index]; + return locationGroups[index]; } @Override @@ -275,12 +278,12 @@ public void setStop(StopLocation stop) { @Override public void setLocation(StopLocation location) { - stops[index] = location; + locations[index] = location; } @Override public void setLocationGroup(StopLocation group) { - stops[index] = group; + locationGroups[index] = group; } @Override diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java index 837e02597..f9fe7d80f 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java @@ -280,7 +280,7 @@ public void setToStopSequence(Integer toStopSequence) { @Override public StopLocation getStop() { - if (proxy != null & stop!=null) { + if (proxy != null) { return proxy.getStop(); } return stop; @@ -288,7 +288,7 @@ public StopLocation getStop() { @Override public StopLocation getLocation() { - if (proxy != null & location!=null) { + if (proxy != null) { return proxy.getLocation(); } return location; @@ -296,7 +296,7 @@ public StopLocation getLocation() { @Override public StopLocation getLocationGroup() { - if (proxy != null & locationGroup!=null) { + if (proxy != null) { return proxy.getLocationGroup(); } return locationGroup; @@ -309,14 +309,14 @@ public StopLocation getLocationGroup() { * - location group */ public StopLocation getStopLocation(){ - if(stop != null){ - return stop; + if(getStop() != null){ + return getStop(); } - else if(location != null) { - return location; + else if(getLocation() != null) { + return getLocation(); } - else if(locationGroup != null){ - return locationGroup; + else if(getLocationGroup() != null){ + return getLocationGroup(); } return null; } @@ -738,7 +738,7 @@ public String displayArrival() { @Override public String toString() { - return "StopTime(seq=" + getStopSequence() + " stop=" + (getStopLocation()==null?"NuLl":getStop().getId()) + return "StopTime(seq=" + getStopSequence() + " stop=" + (getStopLocation()==null?"NuLl":getStopLocation().getId()) + " trip=" + (getTrip()==null?"NuLl":getTrip().getId()) + " times=" + StopTimeFieldMappingFactory.getSecondsAsString(getArrivalTime()) + "-" From 8784ee50f31a0f95cedee7e4eb382ba58ae14eec Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Wed, 1 May 2024 13:51:59 -0400 Subject: [PATCH 017/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-1.4.17 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index a6270a4cb..a345cbcb2 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 4b91806bc..29263746c 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index ed1baabe1..55840de29 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.17-SNAPSHOT + 1.4.17 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 3383831fb..31684959c 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.17-SNAPSHOT + 1.4.17 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index f67feb7bd..7fc787954 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 884e6d20a..cc43c78a6 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index f1aa3fc64..f494020e0 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 org.onebusaway onebusaway-gtfs - 1.4.17-SNAPSHOT + 1.4.17 org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 9e8d10249..32a3d2b7b 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 diff --git a/pom.xml b/pom.xml index b49b04cb0..0791eafc7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.17-SNAPSHOT + 1.4.17 pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-1.4.17 From c955a39947de416b1dfdfd32cd23f4c739302fa3 Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Wed, 1 May 2024 13:52:01 -0400 Subject: [PATCH 018/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli-aws/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index a345cbcb2..85e8c3e9b 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 29263746c..d82f6cfd6 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 55840de29..76e668965 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.17 + 1.4.18-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 31684959c..e51cdf411 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.17 + 1.4.18-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 7fc787954..ab549e652 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index cc43c78a6..a5d1a85bd 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index f494020e0..c3e51749b 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,14 +9,14 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT org.onebusaway onebusaway-gtfs - 1.4.17 + 1.4.18-SNAPSHOT org.onebusaway diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 32a3d2b7b..05111d3a8 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT diff --git a/pom.xml b/pom.xml index 0791eafc7..6f0917cca 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ onebusaway-gtfs-modules - 1.4.17 + 1.4.18-SNAPSHOT pom onebusaway-gtfs-modules @@ -25,7 +25,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-1.4.17 + HEAD From 3d26010223034ac60e30ff9a3c2cd85e2bcb1247 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 18:00:33 +0200 Subject: [PATCH 019/234] Remove unused method --- .../src/main/java/org/onebusaway/gtfs/model/StopTime.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java index b80f961d3..95c0307b1 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopTime.java @@ -740,10 +740,4 @@ public void setFreeRunningFlag(String freeRunningFlag) { this.freeRunningFlag = freeRunningFlag; } - private static void oldDropOffSpellingWarning(String type) { - _log.warn("This feed uses the old spelling of '{}_pickup_drop_off_window' ('dropoff' instead of 'drop_off'). " - + "Compatibility will be removed in the future, so please update your feed to be in line with the latest Flex V2 spec:" - + " https://github.com/MobilityData/gtfs-flex/commit/547200dfb", type); - } - } From 68242f6579a12380711f46a40b587dfd84a18786 Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Mon, 10 Jun 2024 13:41:36 -0400 Subject: [PATCH 020/234] MOTP-2144 allow for differing patterns in UpdateCalendarDatesForDuplicateTrips --- .../UpdateCalendarDatesForDuplicateTrips.java | 488 +++++++++--------- .../onebusaway/gtfs/model/DuplicateTrips.java | 89 ---- 2 files changed, 236 insertions(+), 341 deletions(-) delete mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/DuplicateTrips.java diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java index dea177d0b..11cefd890 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java @@ -17,7 +17,6 @@ package org.onebusaway.gtfs_transformer.impl; import org.onebusaway.gtfs.model.*; -import org.onebusaway.gtfs.model.calendar.ServiceDate; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; import org.onebusaway.gtfs_transformer.services.TransformContext; @@ -25,7 +24,15 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.stream.Collectors; +/** + * Map ATIS trip_ids to mta_trips_ids while de-duplicating. + * Tag each "duplicate" trip with an ATIS id to force it unique if the stopping pattern differs. + * Otherwise create new service_ids representing the service of the duplicates, + * adding to an exemplar trip and deleting the duplicates. + * + */ public class UpdateCalendarDatesForDuplicateTrips implements GtfsTransformStrategy { private final Logger _log = LoggerFactory.getLogger(UpdateCalendarDatesForDuplicateTrips.class); @@ -35,307 +42,284 @@ public String getName() { return this.getClass().getSimpleName(); } - @Override public void run(TransformContext context, GtfsMutableRelationalDao dao) { - RemoveEntityLibrary removeEntityLibrary = new RemoveEntityLibrary(); - String agency = dao.getAllTrips().iterator().next().getId().getAgencyId(); + if (dao == null || dao.getAllTrips().isEmpty()) { + throw new IllegalStateException("nothing to do!"); + } + String calendarAgencyId = dao.getAllTrips().iterator().next().getId().getAgencyId(); + DuplicateState state = new DuplicateState(dao, calendarAgencyId); //map of each mta_trip_id and list of trips - HashMap> tripsMap = new HashMap<>(); - //List of DuplicateTrips - ArrayList duplicateTripData = new ArrayList<>(); + HashMap> tripsByMtaTripId = buildTripMap(state, dao); - //they are only duplicates if the stop times match as well. - //if the stop times match, then we can move forward with merging trips - //if not, then we can't merge and we leave the trips alone + // we only use this for informational logging, we don't actually compare to reference + GtfsMutableRelationalDao reference = (GtfsMutableRelationalDao) context.getReferenceReader().getEntityStore(); - //set all the trips that are duplicates based on mta_trip_id - int mtaIdNull = 0; - for (Trip trip : dao.getAllTrips()) { - if (trip.getMtaTripId() != null) { - if (tripsMap.containsKey(trip.getMtaTripId())) { - ArrayList trips = tripsMap.get(trip.getMtaTripId()); - trips.add(trip); - tripsMap.put(trip.getMtaTripId(), trips); - } else { - ArrayList trips = new ArrayList<>(); - trips.add(trip); - tripsMap.put(trip.getMtaTripId(), trips); - } - } else { - _log.info("trip {} mta_trip_id is null", trip.getId()); - mtaIdNull++; - } + HashMap referenceTripsByTripIdByTripId = new HashMap<>(); + for (Trip trip : reference.getAllTrips()) { + referenceTripsByTripIdByTripId.put(trip.getId().getId(), trip); } - GtfsMutableRelationalDao reference = (GtfsMutableRelationalDao) context.getReferenceReader().getEntityStore(); + logDuplicates(tripsByMtaTripId, referenceTripsByTripIdByTripId); - HashMap referenceTrips = new HashMap<>(); - for (Trip trip : reference.getAllTrips()) { - referenceTrips.put(trip.getId().getId(), trip); + _log.info("Incoming Routes: {} Trips: {} Stops: {} Stop times: {} CalDatess: {} ", dao.getAllRoutes().size(), dao.getAllTrips().size(), dao.getAllStops().size(), dao.getAllStopTimes().size(), dao.getAllCalendarDates().size()); + + // perform the computations, but application of them is delayed till later + update(dao, state, tripsByMtaTripId); + // apply the changes + state.apply(); + + _log.info("Outgoing Routes: {} Trips: {} Stops: {} Stop times: {} CalDates: {} ", dao.getAllRoutes().size(), dao.getAllTrips().size(), dao.getAllStops().size(), dao.getAllStopTimes().size(), dao.getAllCalendarDates().size()); + _log.info("deleted trips: {} duplicate trip Ids: {} null ids {}", state.deletedTripCounter, state.duplicateTripIdCounter, state.mtaIdNullCounter); + } + + private void update(GtfsMutableRelationalDao dao, DuplicateState state, HashMap> tripsByMtaTripId) { + for (Map.Entry> entry : tripsByMtaTripId.entrySet()) { + update(state, entry.getKey(), entry.getValue()); + deDuplicate(dao, state, entry.getKey(), entry.getValue()); } + } - //this is just for logging if dups are in reference, delete when ready - /* Iterator entries2 = tripsMap.entrySet().iterator(); - while (entries2.hasNext()) { - HashMap.Entry entry = (HashMap.Entry) entries2.next(); - ArrayList trips = (ArrayList) entry.getValue(); - if (trips.size() > 1) { - //these are duplicates - if (referenceTrips.containsKey(entry.getKey())) { - //_log.info("Duplicate trip id {} is in reference", entry.getKey()); - } - } + private void update(DuplicateState state, String mtaTripId, ArrayList duplicateTrips) { + for (Trip duplicateTrip : duplicateTrips) { + String agencyId = duplicateTrip.getId().getAgencyId(); + String modifier = duplicateTrip.getId().getId(); + // if we change the id here we can't decide to delete it later + // instead map the change and do it later + state.addTripToTrack(duplicateTrip.getId(), new AgencyAndId(agencyId, mtaTripId + "-dup-" + modifier)); } -*/ - int orStopTimes = dao.getAllStopTimes().size(); - _log.info("Routes: {} Trips: {} Stops: {} Stop times: {} CalDatess: {} ", dao.getAllRoutes().size(), dao.getAllTrips().size(), dao.getAllStops().size(), dao.getAllStopTimes().size(), dao.getAllCalendarDates().size()); + } + + private void deDuplicate(GtfsMutableRelationalDao dao, DuplicateState state, String mtaTripId, ArrayList duplicateTrips) { + Map> patternHashToTripId = new HashMap<>(); + for (Trip duplicateTrip : duplicateTrips) { + String patternHash = hashPattern(dao, duplicateTrip); + if (!patternHashToTripId.containsKey(patternHash)) { + patternHashToTripId.put(patternHash, new ArrayList()); + } + patternHashToTripId.get(patternHash).add(duplicateTrip); + } - int countUnique = 0; - int countCombine = 0; - int countDoNothing = 0; - int countToday = 0; + deDuplicate(dao, state, mtaTripId, patternHashToTripId); + } - Iterator entries = tripsMap.entrySet().iterator(); - int service_id = getNextServiceId(dao); - while (entries.hasNext()) { - HashMap.Entry entry = (HashMap.Entry) entries.next(); - ArrayList trips = (ArrayList) entry.getValue(); + private void deDuplicate(GtfsMutableRelationalDao dao, DuplicateState state, String mtaTripId, Map> patternHashToTripId) { + // each pattern only needs one representative trip -- we don't care which -- and then multiple calendar entries + for (List trips : patternHashToTripId.values()) { if (trips.size() > 1) { - Boolean equals = true; - //do all the trips have identical stops? If yes, proceed and update calendar dates and stop times and the trip_id - //If not, leave the trip alone. Do nothing. - trip_loop: - for (int i = 0; i < trips.size(); i++) { - for (int j = i+1; j < trips.size(); j++) { - //if (!dao.getStopTimesForTrip(trips.get(i)).equals(dao.getStopTimesForTrip(trips.get(j))) ) { - //they won't be equal because a stop time has a trip id and the trip ids are different - if (!stopTimesEqual(dao.getStopTimesForTrip(trips.get(i)), dao.getStopTimesForTrip(trips.get(j)))) { - //_log.info("The stop times for {} and {} are not equal", trips.get(i).getId().getId(), trips.get(j).getId().getId()); - equals = false; - //so at this point the stop times don't equal. Do I check if its just one or just throw the whole thing out? - //For now if one doesn't match then none do and I'm going to ignore. - countDoNothing = countDoNothing + trips.size(); - - //check if any of the trips are today. If one of them is, then copy over the mta_id and ignore the duplicates - //so at least one trip will get the right id - if (checkForServiceToday(trips, dao)) { - countToday++; - } - break trip_loop; - } - } - } - if (equals) { - //_log.info("EQUALS!"); - //for each mta_id that is a duplicate, we need to ultimately delete those duplicates - //First, get all the corresponding serviceDates for all the trips with that mta_id, then create new service ids - //and add entries with that new id that correspond to all the service dates for all the trips - DuplicateTrips dup = new DuplicateTrips((String) entry.getKey(), Integer.toString(service_id), trips); - duplicateTripData.add(dup); - service_id++; - countCombine = countCombine + trips.size(); - } - } - else { - //trips.size is not > 1 so these trips are unique. Copy over mta_trip_id - Trip trip = trips.get(0); - trip.setId(new AgencyAndId(trip.getId().getAgencyId(), trip.getMtaTripId())); - countUnique++; + Trip exemplar = trips.remove(0); + deDuplicateTrip(dao, state, exemplar, trips); } } - _log.info("Mta_trip_ids: null {}, unique {}, do nothing {}, today {}, combine {}, total {}", mtaIdNull, countUnique, countDoNothing, countToday, countCombine, mtaIdNull+countUnique+countDoNothing+countCombine); - - //now we have a list of DuplicateTrips and we need to fill in the calendar dates - for (DuplicateTrips dts : duplicateTripData) { - for (Trip trip : dts.getTrips()) { - //for each trip, get the calendar dates - for (ServiceCalendarDate calDate : dao.getCalendarDatesForServiceId(trip.getServiceId())) { - dts.addServiceDate(calDate); + } + + private void deDuplicateTrip(GtfsMutableRelationalDao dao, DuplicateState state, Trip exemplar, List tripsToRemove) { + Set serviceIds = tripsToRemove.stream().map(l->l.getServiceId()).collect(Collectors.toSet()); + addServiceForTrip(dao, state, exemplar, serviceIds); + deleteTrips(dao, state, tripsToRemove); + } + + private void deleteTrips(GtfsMutableRelationalDao dao, DuplicateState state, List tripsToRemove) { + state.removeTrip(tripsToRemove); + } + + private void addServiceForTrip(GtfsMutableRelationalDao dao, DuplicateState state, Trip exemplar, Set serviceIds) { + state.addTrip(exemplar, serviceIds); + } + + private String hashPattern(GtfsMutableRelationalDao dao, Trip duplicateTrip) { + StringBuffer sb = new StringBuffer(); + for (StopTime stopTime : dao.getStopTimesForTrip(duplicateTrip)) { + sb.append(stopTime.getStop().getId().getId()); + sb.append(":"); + sb.append(stopTime.getArrivalTime()); + sb.append(":"); + sb.append(stopTime.getDepartureTime()); + sb.append(":"); + } + if (sb.length() == 0) + return "empty"; // this is technically an error but support it just in case + return sb.substring(0, sb.length() - 1); + } + + // index ATIS trips by mta_trip_id + private HashMap> buildTripMap(DuplicateState state, GtfsMutableRelationalDao dao) { + HashMap> tripsByMtaTripId = new HashMap<>(); + + for (Trip trip : dao.getAllTrips()) { + if (trip.getMtaTripId() != null) { + if (!tripsByMtaTripId.containsKey(trip.getMtaTripId())) { + tripsByMtaTripId.put(trip.getMtaTripId(), new ArrayList<>()); } + tripsByMtaTripId.get(trip.getMtaTripId()).add(trip); + } else { + _log.info("trip {} mta_trip_id is null", trip.getId()); + state.mtaIdNullCounter++; } } - //now we have a list of DuplicateTrips and their calendar dates - //a lot of the DuplicateTrips will have the same list of calendar dates. Don't create duplicate calendar entries unnecessarily - - //Create a unique list of calendar dates to add - HashMap> dateMap = new HashMap<>(); - - //for each duplicateTrips in the list, get the list of caldate entries - //if the caldate entries is in the dateMap, change the Service Id for the duplicate trip - //if its not in there, then add it - int newDates = 0; - for (DuplicateTrips dts : duplicateTripData) { - //first time through, populate dateMap - if (dateMap.isEmpty()) { - dateMap.put(dts.getServiceId(), dts.getDates()); - } else { - boolean addNewDateMap = true; - for (HashMap.Entry> calDate : dateMap.entrySet()) { - ArrayList scds = (ArrayList) calDate.getValue(); - //scds is a unique list of service calendar dates in the map - if (new HashSet(dts.getDates()).equals(new HashSet(scds))) { - //we already have a list of the same dates. Re-use the service id - addNewDateMap = false; - //set the service date id in DuplicateTrips to be this one - dts.setServiceId(calDate.getKey()); - break; + + return tripsByMtaTripId; + } + + private void logDuplicates(HashMap> tripsByMtaTripId, HashMap referenceTripsByTripId) { + if (_log.isDebugEnabled()) { + //this is just for logging if dups are in reference + Iterator entries2 = tripsByMtaTripId.entrySet().iterator(); + while (entries2.hasNext()) { + HashMap.Entry entry = (HashMap.Entry) entries2.next(); + ArrayList trips = (ArrayList) entry.getValue(); + if (trips.size() > 1) { + //these are duplicates + if (referenceTripsByTripId.containsKey(entry.getKey())) { + _log.info("Duplicate trip id {} is in reference", entry.getKey()); } } - //there was no match, update the date map and add new serviceId - if (addNewDateMap) { - //dates don't exist, add new entry to date map and add service id - dateMap.put(dts.getServiceId(), dts.getDates()); - newDates = newDates + dts.getDates().size(); - } } } + } - int serviceIds = 0; - //Now the list is compete, add the new service id and dates - for (HashMap.Entry> calDateId : dateMap.entrySet()) { - AgencyAndId newServiceId = new AgencyAndId(agency, calDateId.getKey()); - ArrayList scds = calDateId.getValue(); - //need a list of the service cal dates, iterate, add - for (ServiceCalendarDate calDate : scds) { - serviceIds++; - //for each date, create a new calendar_dates entry with the new service_id - ServiceCalendarDate newScd = new ServiceCalendarDate(); - newScd.setServiceId(newServiceId); - newScd.setDate(calDate.getDate()); - newScd.setExceptionType(calDate.getExceptionType()); - dao.saveOrUpdateEntity(newScd); - } + + /** + * Internal state of the algorithm. + */ + private static class DuplicateState { + private int mtaIdNullCounter = 0; + private int serviceIdCounter = 0; + private int duplicateTripIdCounter = 0; + private int deletedTripCounter = 0; + private Map, List> tripsByServiceIds = new HashMap<>(); + private List tripsToRemove = new ArrayList<>(); + private GtfsMutableRelationalDao dao; + private String calendarAgencyId; + private Map atisToMtaTripId = new HashMap<>(); + + public DuplicateState(GtfsMutableRelationalDao dao, String calendarAgencyId) { + this.dao = dao; + this.calendarAgencyId = calendarAgencyId; + String largestServiceId = Collections.max(dao.getAllCalendarDates().stream().map(l->l.getServiceId().getId()).collect(Collectors.toSet())); + // here we make an assumption that service ids are numeric + serviceIdCounter = Integer.parseInt(largestServiceId) + 1; } - //trips updated, array of mta_ids that we've updated - HashMap tripsUpdated = new HashMap<>(); - ArrayList tripsToRemove = new ArrayList<>(); - - //update the trips with the new service_id - for (DuplicateTrips dts : duplicateTripData) { - AgencyAndId newServiceId = new AgencyAndId(agency, dts.getServiceId()); - for (Trip trip : dts.getTrips()) { - //for each trip, set the new service id - trip.setServiceId(newServiceId); - //now the trip_id has to be set with the mta_trip_id - //we have to have one as the one to keep and mark the others for deletion - //and then there needs to be a seperate method for all the deletions. - if (trip.getMtaTripId() != null) { - if (tripsUpdated.containsKey(trip.getMtaTripId())) { - tripsToRemove.add(trip); - } else { - tripsUpdated.put(trip.getMtaTripId(), trip); - trip.setId(new AgencyAndId(trip.getId().getAgencyId(), trip.getMtaTripId())); - } - } + public void addTrip(Trip exemplar, Set serviceIds) { + if (!tripsByServiceIds.containsKey(serviceIds)) { + tripsByServiceIds.put(serviceIds, new ArrayList<>()); } + tripsByServiceIds.get(serviceIds).add(exemplar); } - int stopsTimesToRemove = 0; - int remove = 0; - for (Trip tripToRemove : tripsToRemove) { - stopsTimesToRemove = stopsTimesToRemove + dao.getStopTimesForTrip(tripToRemove).size(); - removeEntityLibrary.removeTrip(dao, tripToRemove); - remove++; + public void removeTrip(List incoming) { + tripsToRemove.addAll(incoming); } - _log.info("Added Service Cal dates: {}, Removed trips: {}, Removed stoptimes: {}", serviceIds, remove, stopsTimesToRemove); - _log.info("Routes: {} Trips: {} Stops: {} Stop times: {} CalDates: {} ", dao.getAllRoutes().size(), dao.getAllTrips().size(), dao.getAllStops().size(), dao.getAllStopTimes().size(), dao.getAllCalendarDates().size()); - } - - private boolean checkForServiceToday(ArrayList trips, GtfsMutableRelationalDao dao) { - //if the stop times are not equal, check and see if any of the trips are running today. - //if the trip is running today, then copy over the id for this one trip, - //we'll ignore the rest of the trips and break the trip loop. - Date today = removeTime(new Date()); - if (trips.size() > 2) { - _log.info("There are more than two matches for this trip id {}", trips.get(0).getMtaTripId()); + public void apply() { + generateServiceIds(); + deleteTrips(); + applyNewTripIds(); } - for (Trip trip : trips) { - for (ServiceCalendarDate calDate : dao.getCalendarDatesForServiceId(trip.getServiceId())) { - Date date = constructDate(calDate.getDate()); - if (calDate.getExceptionType() == 1 && date.equals(today)) { - //_log.info("Copying over id for {} {}", trip.getId(), trip.getMtaTripId()); - //trip is today, copy of the mta_id for this one and quit - trip.setId(new AgencyAndId(trip.getId().getAgencyId(), trip.getMtaTripId())); - return true; + + private void applyNewTripIds() { + Map mtaTripIdCounts = new HashMap<>(); + for (Map.Entry entry : atisToMtaTripId.entrySet()) { + AgencyAndId atisTripId = entry.getKey(); + AgencyAndId mtaTripId = entry.getValue(); + AgencyAndId rawMtaTripId = removeTag(mtaTripId); + if (!mtaTripIdCounts.containsKey(rawMtaTripId)) { + mtaTripIdCounts.put(rawMtaTripId, 1); + } else { + mtaTripIdCounts.put(rawMtaTripId, mtaTripIdCounts.get(rawMtaTripId)+1); } } - } - return false; - } - private int getNextServiceId(GtfsMutableRelationalDao dao) { - ArrayList idList = new ArrayList<>(); - for (ServiceCalendarDate svcDate : dao.getAllCalendarDates()) { - if (isInt(svcDate.getServiceId().getId())) { - idList.add(Integer.parseInt(svcDate.getServiceId().getId())); + + for (Map.Entry agencyAndIdAgencyAndIdEntry : atisToMtaTripId.entrySet()) { + AgencyAndId atisTripId = agencyAndIdAgencyAndIdEntry.getKey(); + AgencyAndId mtaTripId = agencyAndIdAgencyAndIdEntry.getValue(); + AgencyAndId rawMtaTripId = removeTag(mtaTripId); + int occurrenceCount = mtaTripIdCounts.get(rawMtaTripId); + Trip toModify = dao.getTripForId(atisTripId); + if (toModify != null) { + if (occurrenceCount > 1) { + // it is a duplicate + incDuplicateCount(); + toModify.setId(mtaTripId); + } else { + // we've pruned it to be unique, remove the dup tagging + toModify.setId(removeTag(mtaTripId)); + } + } else { + // the trip has already been deleted, nothing to do + System.out.println("non-existent trip " + atisTripId + "/" + mtaTripId); + } + } - } - return Collections.max(idList) + 1; - } - private boolean isInt(String str) { - if (str == null) { - return false; } - int length = str.length(); - if (length == 0) { - return false; + + private void incDuplicateCount() { + duplicateTripIdCounter++; } - for (int i = 0; i < length; i++) { - char c = str.charAt(i); - if (c < '0' || c > '9') { - return false; + + private AgencyAndId removeTag(AgencyAndId mtaTripId) { + int pos = mtaTripId.getId().lastIndexOf("-dup-"); + if (pos > 1) { + return new AgencyAndId(mtaTripId.getAgencyId(), mtaTripId.getId().substring(0, pos)); } + return mtaTripId; } - return true; - } - private boolean stopTimesEqual(List s1, List s2) { - if (s1.size() != s2.size()) { - //_log.info("Not equal on size {} {}", s1.size(), s2.size()); - return false; + private void deleteTrips() { + RemoveEntityLibrary removeEntityLibrary = new RemoveEntityLibrary(); + + for (Trip tripToRemove : tripsToRemove) { + deletedTripCounter++; + removeEntityLibrary.removeTrip(dao, tripToRemove); + atisToMtaTripId.remove(tripToRemove.getId()); + } } - int index = 0; - for (int i = 0; i < s1.size(); i++) { - if(!s1.get(i).getStop().equals(s2.get(i).getStop())) { - //_log.info("Stops {} {}", s1.get(i).getStop(), s2.get(i).getStop()); - return false; + + private void generateServiceIds() { + for (Map.Entry, List> tripsBySet : tripsByServiceIds.entrySet()) { + Set calendarIds = tripsBySet.getKey(); + List trips = tripsBySet.getValue(); + AgencyAndId newServiceId = generateServiceId(calendarIds); + for (Trip trip : trips) { + trip.setServiceId(newServiceId); + } } - if(s1.get(i).getDepartureTime() != s2.get(i).getDepartureTime()) { - //_log.info("Dep time {} {}", s1.get(i).getDepartureTime(), s2.get(i).getDepartureTime()); - return false; + + } + + private AgencyAndId generateServiceId(Set calendarIds) { + List dates = new ArrayList<>(); + for (AgencyAndId calendarId : calendarIds) { + List calendarDatesForServiceId = dao.getCalendarDatesForServiceId(calendarId); + dates.addAll(calendarDatesForServiceId); } - if(s1.get(i).getArrivalTime() != s2.get(i).getArrivalTime()) { - //_log.info("Arr time {} {}", s1.get(i).getArrivalTime(), s2.get(i).getArrivalTime()); - return false; + + AgencyAndId newServiceId = generateServiceId(); + + for (ServiceCalendarDate calDate : dates) { + ServiceCalendarDate newDate = new ServiceCalendarDate(); + newDate.setServiceId(newServiceId); + newDate.setDate(calDate.getDate()); + newDate.setExceptionType(calDate.getExceptionType()); + dao.saveOrUpdateEntity(newDate); } + + return newServiceId; } - return true; - } - private Date constructDate(ServiceDate date) { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.YEAR, date.getYear()); - calendar.set(Calendar.MONTH, date.getMonth()-1); - calendar.set(Calendar.DATE, date.getDay()); - Date date1 = calendar.getTime(); - date1 = removeTime(date1); - return date1; - } + private AgencyAndId generateServiceId() { + serviceIdCounter++; + return new AgencyAndId(calendarAgencyId, String.valueOf(serviceIdCounter)); + } - private Date removeTime(Date date) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - date = calendar.getTime(); - return date; + public void addTripToTrack(AgencyAndId atisId, AgencyAndId mtaTripId) { + atisToMtaTripId.put(atisId, mtaTripId); + } } + } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/DuplicateTrips.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/DuplicateTrips.java deleted file mode 100644 index ae5f82950..000000000 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/DuplicateTrips.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2018 Cambridge Systematics, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onebusaway.gtfs.model; - -import java.util.ArrayList; - -public class DuplicateTrips { - - private String id; - - private String serviceId; - - private ArrayList trips = new ArrayList(); - - private ArrayList dates = new ArrayList(); - - public DuplicateTrips() { - - } - - public DuplicateTrips(String id, String svcId, ArrayList trips) { - this.setId(id); - this.setServiceId(svcId); - this.setTrips(trips); - } - - public DuplicateTrips(DuplicateTrips dts) { - this.setId(dts.getId()); - this.setServiceId(dts.getServiceId()); - this.setTrips(dts.getTrips()); - this.setDates(dts.getDates()); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public ArrayList getTrips() { - return trips; - } - - public void setTrips(ArrayList trips) { - this.trips = trips; - } - - public void addTrip(Trip trip) { - this.trips.add(trip); - } - - public ArrayList getDates() { - return dates; - } - - public void setDates(ArrayList dates) { - this.dates = dates; - } - - public void addServiceDate(ServiceCalendarDate date) { - this.dates.add(date); - } - - -} From eece7bb848f812d16ce41b85a9e10a90d4bfba30 Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Mon, 10 Jun 2024 14:49:16 -0400 Subject: [PATCH 021/234] bug fix on MOTP-2144 --- .../impl/UpdateCalendarDatesForDuplicateTrips.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java index 11cefd890..cfdedf459 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java @@ -199,8 +199,16 @@ public DuplicateState(GtfsMutableRelationalDao dao, String calendarAgencyId) { this.dao = dao; this.calendarAgencyId = calendarAgencyId; String largestServiceId = Collections.max(dao.getAllCalendarDates().stream().map(l->l.getServiceId().getId()).collect(Collectors.toSet())); - // here we make an assumption that service ids are numeric - serviceIdCounter = Integer.parseInt(largestServiceId) + 1; + // remove any de-duplication prefix from the service id + largestServiceId = largestServiceId.replaceAll("^[a-z]-", ""); + try { + // here we make an assumption that service ids are numeric + serviceIdCounter = Integer.parseInt(largestServiceId) + 1; + } catch (NumberFormatException e) { + // we guessed wrong, we have some string service ids + // create a higher order service_id that should not conflict + serviceIdCounter = 10000; + } } public void addTrip(Trip exemplar, Set serviceIds) { From 57c4a43648e544549ea3fd41155a594272f886c6 Mon Sep 17 00:00:00 2001 From: lcaraballo Date: Mon, 10 Jun 2024 15:22:37 -0400 Subject: [PATCH 022/234] Adding Icons ext and Updating Vehicles ext to support Icons Updated the Vehicles Extension to support an additional optional column icon_id, Updated GTFSDataService to support getting all vehicles and vehilcle by id, Added unit tests for vehicles and icon --- .../org/onebusaway/gtfs/impl/GtfsDaoImpl.java | 4 ++ .../gtfs/impl/GtfsDataServiceImpl.java | 10 ++++ .../java/org/onebusaway/gtfs/model/Icon.java | 46 +++++++++++++++++++ .../org/onebusaway/gtfs/model/Vehicle.java | 15 +++++- .../GtfsEntitySchemaFactory.java | 2 + .../gtfs/serialization/GtfsReader.java | 4 ++ .../mappings/IconFieldMappingImpl.java | 7 +++ .../org/onebusaway/gtfs/services/GtfsDao.java | 11 +++++ .../org/onebusaway/gtfs/GtfsTestData.java | 6 +++ .../serialization/VehiclesExtReaderTest.java | 41 +++++++++++++++++ .../gtfs/testagency-vehicles-ext/agency.txt | 2 + .../gtfs/testagency-vehicles-ext/calendar.txt | 3 ++ .../testagency-vehicles-ext/feed_info.txt | 2 + .../gtfs/testagency-vehicles-ext/icons.txt | 2 + .../gtfs/testagency-vehicles-ext/routes.txt | 8 ++++ .../gtfs/testagency-vehicles-ext/shapes.txt | 10 ++++ .../testagency-vehicles-ext/stop_times.txt | 44 ++++++++++++++++++ .../gtfs/testagency-vehicles-ext/stops.txt | 14 ++++++ .../testagency-vehicles-ext/transfers.txt | 5 ++ .../testagency-vehicles-ext/translations.txt | 21 +++++++++ .../gtfs/testagency-vehicles-ext/trips.txt | 16 +++++++ .../gtfs/testagency-vehicles-ext/vehicles.txt | 2 + 22 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java create mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java create mode 100644 onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/calendar.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/feed_info.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/routes.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/shapes.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stop_times.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stops.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/transfers.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/translations.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/trips.txt create mode 100644 onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java index 89d411605..772a21ddc 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java @@ -253,6 +253,10 @@ public Level getLevelForId(AgencyAndId id) { return getEntityForId(Level.class, id); } + public Vehicle getVehicleForId(AgencyAndId id) { + return getEntityForId(Vehicle.class, id); + } + public Facility getFacilityForId(AgencyAndId id) { return getEntityForId(Facility.class, id);} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java index 033eb3976..2cd915d98 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java @@ -353,6 +353,16 @@ public Collection getAllRiderships() { return _dao.getAllRiderships(); } + @Override + public Collection getAllVehicles() { + return _dao.getAllVehicles(); + } + + @Override + public Vehicle getVehicleForId(AgencyAndId id) { + return _dao.getVehicleForId(id); + } + @Override public Collection getAllAreas() { return _dao.getAllAreas(); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java new file mode 100644 index 000000000..638a2e9b2 --- /dev/null +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java @@ -0,0 +1,46 @@ +package org.onebusaway.gtfs.model; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; + +/** + * GTFS Extension representing icon configuration data. + */ +@CsvFields(filename = "icons.txt", required = false, prefix = "icon_") +public final class Icon extends IdentityBean{ + private static final long serialVersionUID = 1L; + + @CsvField(mapping = DefaultAgencyIdFieldMappingFactory.class) + private AgencyAndId id; + @CsvField(optional = true) + private String description; + @CsvField + private String url; + + @Override + public AgencyAndId getId() { + return id; + } + + @Override + public void setId(AgencyAndId id) { + this.id = id; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Vehicle.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Vehicle.java index 659366dfe..3a81c0d40 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Vehicle.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Vehicle.java @@ -18,13 +18,15 @@ import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.csv_entities.schema.annotations.CsvFields; import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; +import org.onebusaway.gtfs.serialization.mappings.EntityFieldMappingFactory; +import org.onebusaway.gtfs.serialization.mappings.StopLocationFieldMappingFactory; /** * GTFS Extension representing vehicle configuration data. */ @CsvFields(filename = "vehicles.txt", required = false) public final class Vehicle extends IdentityBean { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; @CsvField(name = "vehicle_id", mapping = DefaultAgencyIdFieldMappingFactory.class) private AgencyAndId id; @@ -53,6 +55,9 @@ public final class Vehicle extends IdentityBean { @CsvField(name = "wheelchair_access", optional = true) private String wheelchairAccess; + @CsvField(name = "icon_id", optional = true, mapping = EntityFieldMappingFactory.class, order = -1) + private Icon icon; + @Override public AgencyAndId getId() { return id; @@ -86,4 +91,12 @@ public void setId(AgencyAndId id) { public String getWheelchairAccess() { return wheelchairAccess; } public void setWheelchairAccess(String wheelchairAccess) { this.wheelchairAccess = wheelchairAccess; } + + public Icon getIcon() { + return icon; + } + + public void setIcon(Icon icon) { + this.icon = icon; + } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java index f246718a9..f630683d1 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java @@ -75,6 +75,7 @@ public static List> getEntityClasses() { entityClasses.add(WrongWayConcurrency.class); entityClasses.add(DirectionEntry.class); entityClasses.add(AlternateStopNameException.class); + entityClasses.add(Icon.class); return entityClasses; } @@ -96,6 +97,7 @@ public static Map, Comparator> getEntityComparators() { comparators.put(ServiceCalendarDate.class, new ServiceCalendarDateComparator()); comparators.put(Vehicle.class, getComparatorForIdentityBeanType(Vehicle.class)); + comparators.put(Icon.class, getComparatorForIdentityBeanType(Icon.class)); return comparators; } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index 2cb17d277..d44571bca 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -65,6 +65,7 @@ public GtfsReader() { _entityClasses.add(Agency.class); _entityClasses.add(Block.class); _entityClasses.add(ShapePoint.class); + _entityClasses.add(Icon.class); _entityClasses.add(Note.class); _entityClasses.add(Area.class); _entityClasses.add(BookingRule.class); @@ -386,6 +387,9 @@ public void handleEntity(Object entity) { } else if (entity instanceof FacilityPropertyDefinition){ FacilityPropertyDefinition facilityPropertyDefinition = (FacilityPropertyDefinition) entity; registerAgencyId(FacilityPropertyDefinition.class, facilityPropertyDefinition.getId()); + } else if (entity instanceof Icon){ + Icon icon = (Icon) entity; + registerAgencyId(Icon.class, icon.getId()); } if (entity instanceof IdentityBean) { diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java new file mode 100644 index 000000000..c18938b90 --- /dev/null +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java @@ -0,0 +1,7 @@ +package org.onebusaway.gtfs.serialization.mappings; + +public class IconFieldMappingImpl extends EntityFieldMappingImpl{ + public IconFieldMappingImpl(Class entityType, String csvFieldName, String objFieldName, Class objFieldType, boolean required) { + super(entityType, csvFieldName, objFieldName, objFieldType, required); + } +} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java index f701b4ab5..9311b7381 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java @@ -184,6 +184,17 @@ public interface GtfsDao extends GenericDao { public Collection getAllRiderships(); + /**** + * {@link Vehicle} Methods + ****/ + Collection getAllVehicles(); + + /**** + * {@link Vehicle} Methods + ****/ + Vehicle getVehicleForId(AgencyAndId id); + + /**** * {@link Area} Methods ****/ diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/GtfsTestData.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/GtfsTestData.java index 4e21dea45..a91b36d07 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/GtfsTestData.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/GtfsTestData.java @@ -53,6 +53,8 @@ private static String gtfsPath(String name) { public static final String LOCATIONS_GEOJSON = gtfsPath("locations.geojson"); + public static final String TEST_AGENCY_VEHICLES_EXT_GTFS = gtfsPath("testagency-vehicles-ext"); + public static File getCaltrainGtfs() { return getResourceAsTemporaryFile(CALTRAIN_GTFS); } @@ -94,6 +96,10 @@ public static File getAuburnTransitFlex() { return new File("src/test/resources", AUBURN_TRANSIT_FLEX); } + public static File getTestAgencyVehiclesExt(){ + return new File("src/test/resources", TEST_AGENCY_VEHICLES_EXT_GTFS); + } + public static void readGtfs(T entityStore, File resourcePath, String defaultAgencyId) throws IOException { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java new file mode 100644 index 000000000..46f104dfa --- /dev/null +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java @@ -0,0 +1,41 @@ +package org.onebusaway.gtfs.serialization; + +import org.junit.Test; +import org.onebusaway.gtfs.GtfsTestData; +import org.onebusaway.gtfs.model.Agency; +import org.onebusaway.gtfs.model.AgencyAndId; +import org.onebusaway.gtfs.model.Vehicle; +import org.onebusaway.gtfs.services.GtfsRelationalDao; + +import java.io.IOException; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + + +public class VehiclesExtReaderTest extends BaseGtfsTest{ + + @Test + public void vehiclesTest() throws IOException { + String agencyId = "agency"; + GtfsRelationalDao dao = processFeed(GtfsTestData.getTestAgencyVehiclesExt(), agencyId, false); + Agency agency = dao.getAgencyForId(agencyId); + assertEquals(agencyId, agency.getId()); + assertEquals("Fake Agency", agency.getName()); + + // All Vehicles + Collection vehicles = dao.getAllVehicles(); + assertEquals(1, vehicles.size()); + + // Vehicle Lookup by ID + Vehicle vehicle = dao.getVehicleForId(new AgencyAndId("agency","123")); + assertNotNull(vehicle); + + // Icon Testing + assertEquals(new AgencyAndId("agency","ICO"),vehicle.getIcon().getId()); + assertEquals("test icon",vehicle.getIcon().getDescription()); + assertEquals("https://iconurl",vehicle.getIcon().getUrl()); + + } +} diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt new file mode 100644 index 000000000..31e2a82e4 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt @@ -0,0 +1,2 @@ +agency_id,agency_name,agency_url,agency_timezone +agency,Fake Agency,http://fake.example.com,America/New_York diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/calendar.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/calendar.txt new file mode 100644 index 000000000..6b351dffa --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/calendar.txt @@ -0,0 +1,3 @@ +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +alldays,1,1,1,1,1,1,1,20090101,20500101 +weekdays,1,1,1,1,1,0,0,20090101,20500101 diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/feed_info.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/feed_info.txt new file mode 100644 index 000000000..124bf78b1 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/feed_info.txt @@ -0,0 +1,2 @@ +feed_publisher_name,feed_publisher_url,feed_lang,default_lang +Fake Feed Publisher,http://fake.example.com,mul,en \ No newline at end of file diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt new file mode 100644 index 000000000..979eb831b --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt @@ -0,0 +1,2 @@ +icon_id,icon_description,icon_url +ICO,test icon,https://iconurl \ No newline at end of file diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/routes.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/routes.txt new file mode 100644 index 000000000..0a4dd22de --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/routes.txt @@ -0,0 +1,8 @@ +route_id,route_short_name,route_long_name,route_type +1,1,1,3 +2,2,2,3 +3,3,3,3 +4,4,4,4 +5,5,5,5 +6,6,6,6 +7,7,7,7 diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/shapes.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/shapes.txt new file mode 100644 index 000000000..5e0159c3f --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/shapes.txt @@ -0,0 +1,10 @@ +shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled +4,41.0,-75.0,1, +4,42.0,-75.0,2, +4,42.5,-75.3,3, +4,43.0,-75.0,4, +5,41.0,-72.0,1,0 +5,41.5,-72.5,2,1.234 +5,41.0,-73.0,3,17.62 +5,41.5,-73.5,4,35.234 +5,41.0,-74.0,5,52.01 diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stop_times.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stop_times.txt new file mode 100644 index 000000000..733401298 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stop_times.txt @@ -0,0 +1,44 @@ +trip_id,arrival_time,departure_time,stop_id,stop_sequence,shape_dist_traveled,pickup_type,drop_off_type,stop_headsign +1.1,00:00:00,00:00:00,A,1,,,, +1.1,00:10:00,00:10:00,B,2,,,, +1.1,00:20:00,00:20:00,C,3,,,, +1.2,00:20:00,00:20:00,A,1,0,,, +1.2,,,B,2,,3,2, +1.2,00:40:00,00:40:00,C,3,52.1,,, +1.3,08:00:00,08:00:00,A,1,,,, +1.3,08:10:00,08:20:00,B,2,,,, +1.3,08:30:00,08:30:00,C,3,,,, +2.1,00:20:00,00:20:00,B,1,,,, +2.1,00:30:00,00:30:00,C,2,,,, +2.1,00:40:00,00:40:00,D,3,,,, +2.2,00:50:00,00:50:00,B,1,,,, +2.2,01:00:00,01:00:00,C,2,,,, +2.2,01:10:00,01:10:00,D,3,,,, +3.1,00:40:00,00:40:00,B,1,,,, +3.1,00:50:00,00:50:00,C,2,,,, +3.1,01:00:00,01:00:00,D,3,,,, +3.1,01:10:00,01:10:00,E,4,,,, +3.2,01:00:00,01:00:00,B,1,,,, +3.2,01:10:00,01:10:00,C,2,,,, +3.2,01:20:00,01:20:00,D,3,,,, +3.2,01:30:00,01:30:00,E,4,,,, +4.1,05:00:00,05:00:00,F,1,,,, +4.1,05:30:00,05:30:00,G,2,,,, +4.1,06:00:00,06:00:00,H,3,,,, +4.2,23:00:00,23:00:00,F,1,,,, +4.2,23:30:00,23:30:00,G,2,,,, +4.2,24:00:00,24:00:00,H,3,,,, +4.3,23:40:00,23:40:00,F,1,,,,to G +4.3,24:10:00,24:10:00,G,2,,,,to H +4.3,24:40:00,24:40:00,H,3,,,, +5.1,08:00:00,08:00:00,I,1,0,,, +5.1,08:10:00,08:10:00,J,2,22.5,,, +5.1,08:20:00,08:20:00,K,3,52.01,,, +6.1,12:00:00,12:00:00,I,1,,,, +6.1,12:10:00,12:10:00,J,2,,,, +6.2,13:00:00,13:00:00,I,1,,,, +6.2,13:10:00,13:10:00,J,2,,,, +7.1,12:20:00,12:20:00,J,1,,,, +7.1,12:30:00,12:30:00,K,2,,,, +7.2,13:20:00,13:20:00,J,1,,,, +7.2,13:30:00,13:30:00,K,2,,,, diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stops.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stops.txt new file mode 100644 index 000000000..37f31d1a3 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/stops.txt @@ -0,0 +1,14 @@ +stop_id,stop_name,stop_lat,stop_lon,wheelchair_boarding +A,A,40,-73,1 +B,B,40,-74,1 +C,C,40,-75,0 +D,D,40,-76,1 +E,E,40,-77,1 +F,F,41,-75, +G,G,42,-75, +H,H,43,-75, +I,I,41,-72, +J,J,41,-73, +K,K,41,-74, +L,L,41.000001,-73.000001, +M,M,41.000002,-73.000002, diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/transfers.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/transfers.txt new file mode 100644 index 000000000..f085afa3a --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/transfers.txt @@ -0,0 +1,5 @@ +to_stop_id,from_stop_id,transfer_type,min_transfer_time +K,L,0, +L,K,0, +M,K,3, +K,M,3, diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/translations.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/translations.txt new file mode 100644 index 000000000..b9ab78a70 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/translations.txt @@ -0,0 +1,21 @@ +table_name,field_name,language,translation,record_id,record_sub_id,field_value +agency,agency_name,es,Fake Agency Spanish,agency,, +agency,agency_name,fr,Fake Agency French,,,Fake Agency +stops,stop_name,es,A Spanish,A,, +stops,stop_name,fr,A French,,,A +routes,route_long_name,es,3 Spanish,3,, +routes,route_long_name,fr,3 French,,,3 +trips,trip_headsign,es,headsign Spanish,3.1,, +trips,trip_headsign,fr,headsign French,,,headsign +stop_times,stop_headsign,es,to G Spanish,4.3,1, +stop_times,stop_headsign,es,to H Spanish,4.3,2, +stop_times,stop_headsign,fr,to G French,,,to G +stop_times,stop_headsign,fr,to H French,,,to H +feed_info,feed_publisher_name,es,Fake Feed Publisher Spanish,,, +feed_info,feed_publisher_url,es,http://fake.example.es,,, +feed_info,feed_publisher_name,fr,Fake Feed Publisher French,,, +feed_info,feed_publisher_url,fr,http://fake.example.fr,,, +wrong_table_name,agency_name,es,Fake Agency Spanish,agency,, +agency,wrong_column_name,es,Fake Agency Spanish,agency,, +agency,agency_name,es,No record id,,, + diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/trips.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/trips.txt new file mode 100644 index 000000000..239380c87 --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/trips.txt @@ -0,0 +1,16 @@ +route_id,service_id,trip_id,shape_id,block_id,wheelchair_accessible,trip_headsign +1,alldays,1.1,,,1,headsign +1,alldays,1.2,,,1,headsign +1,alldays,1.3,,,1,headsign +2,alldays,2.1,,,0,headsign +2,alldays,2.2,,,0,headsign +3,alldays,3.1,,,1,headsign +3,alldays,3.2,,,1,headsign +4,weekdays,4.1,4,,,headsign +4,weekdays,4.2,4,,,headsign +4,weekdays,4.3,4,,,headsign +5,alldays,5.1,5,,,headsign +6,alldays,6.1,,block.1,,headsign +7,alldays,7.1,,block.1,,headsign +6,alldays,6.2,,block.2,,headsign +7,alldays,7.2,,block.2,,headsign diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt new file mode 100644 index 000000000..a06af272a --- /dev/null +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt @@ -0,0 +1,2 @@ +vehicle_id,icon_id +123,ICO \ No newline at end of file From 52f5234002a1f36766862b1e72ed4454f0464c0a Mon Sep 17 00:00:00 2001 From: lcaraballo Date: Mon, 10 Jun 2024 19:57:54 -0400 Subject: [PATCH 023/234] Removing unneeded field mapping --- .../gtfs/serialization/mappings/IconFieldMappingImpl.java | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java deleted file mode 100644 index c18938b90..000000000 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/IconFieldMappingImpl.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.onebusaway.gtfs.serialization.mappings; - -public class IconFieldMappingImpl extends EntityFieldMappingImpl{ - public IconFieldMappingImpl(Class entityType, String csvFieldName, String objFieldName, Class objFieldType, boolean required) { - super(entityType, csvFieldName, objFieldName, objFieldType, required); - } -} From 6af398625600e915c26fad7ca7d079958bf72890 Mon Sep 17 00:00:00 2001 From: lcaraballo Date: Mon, 10 Jun 2024 20:34:10 -0400 Subject: [PATCH 024/234] Adding missing license headers --- .../main/java/org/onebusaway/gtfs/model/Icon.java | 15 +++++++++++++++ .../gtfs/serialization/VehiclesExtReaderTest.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java index 638a2e9b2..337031f7b 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Icon.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2024 Cambridge Systematics, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.onebusaway.gtfs.model; import org.onebusaway.csv_entities.schema.annotations.CsvField; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java index 46f104dfa..97534e277 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2024 Cambridge Systematics, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.onebusaway.gtfs.serialization; import org.junit.Test; From ff5be11e629f3ec4d532bc3d4080e32c0be32530 Mon Sep 17 00:00:00 2001 From: lcaraballo Date: Mon, 10 Jun 2024 21:35:01 -0400 Subject: [PATCH 025/234] Adding gtfs-hibernate mapping for Vehicle and Icon --- .../impl/HibernateGtfsRelationalDaoImpl.java | 10 ++ .../gtfs/model/GtfsMapping.hibernate.xml | 26 +++++ .../org/onebusaway/gtfs/impl/VehicleTest.java | 109 ++++++++++++++++++ .../gtfs/testagency-vehicles-ext/agency.txt | 2 + .../gtfs/testagency-vehicles-ext/icons.txt | 3 + .../gtfs/testagency-vehicles-ext/vehicles.txt | 3 + 6 files changed, 153 insertions(+) create mode 100644 onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java create mode 100644 onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt create mode 100644 onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt create mode 100644 onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 6ddc0b329..6d89eb108 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -161,6 +161,16 @@ public Collection getAllTransfers() { @Override public Collection getAllRiderships() { return _ops.find("FROM Ridership"); } + @Override + public Collection getAllVehicles() { + return _ops.find("FROM Vehicle"); + } + + @Override + public Vehicle getVehicleForId(AgencyAndId id) { + return (Vehicle) _ops.get(Vehicle.class, id); + } + @Override public Collection getAllDirectionEntries() { diff --git a/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml b/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml index d17e6902d..5ad73ca66 100644 --- a/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml +++ b/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml @@ -365,4 +365,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java new file mode 100644 index 000000000..5693a32a9 --- /dev/null +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2024 Cambridge Systematics, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs.impl; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onebusaway.gtfs.model.*; +import org.onebusaway.gtfs.serialization.GtfsReader; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class VehicleTest { + private static SessionFactory _sessionFactory; + + private static final String _agencyId = "agency"; + + private static HibernateGtfsRelationalDaoImpl _dao; + + @BeforeClass + public static void setup() throws IOException { + + Configuration config = new Configuration(); + config = config.configure("org/onebusaway/gtfs/hibernate-configuration.xml"); + _sessionFactory = config.buildSessionFactory(); + + _dao = new HibernateGtfsRelationalDaoImpl(_sessionFactory); + + GtfsReader reader = new GtfsReader(); + reader.setInputLocation(new File("src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext")); + reader.setEntityStore(_dao); + reader.setDefaultAgencyId(_agencyId); + + List> entityClasses = reader.getEntityClasses(); + entityClasses.clear(); + entityClasses.add(Agency.class); + entityClasses.add(Icon.class); + entityClasses.add(Vehicle.class); + + reader.run(); + } + + @AfterClass + public static void teardown() { + _sessionFactory.close(); + } + + @Test + public void testVehicleById() { + Vehicle vehicle = _dao.getVehicleForId(aid("123")); + assertEquals(aid("123"), vehicle.getId()); + assertEquals(2, vehicle.getBikeCapacity()); + assertEquals("vehicle description", vehicle.getDescription()); + assertEquals(2, vehicle.getDoorCount()); + assertEquals("30 ft", vehicle.getDoorWidth()); + assertEquals(40, vehicle.getSeatedCapacity()); + assertEquals(20, vehicle.getStandingCapacity()); + assertEquals(0, vehicle.getLowFloor()); + assertEquals("yes", vehicle.getWheelchairAccess()); + + // Icon + Icon icon = vehicle.getIcon(); + assertEquals(aid("ICO"), icon.getId()); + assertEquals("test icon", icon.getDescription()); + assertEquals("https://iconurl", icon.getUrl()); + + // Larger Vehicle + vehicle = _dao.getVehicleForId(aid("456")); + assertEquals(aid("456"), vehicle.getId()); + assertEquals(4, vehicle.getBikeCapacity()); + assertEquals("larger vehicle description", vehicle.getDescription()); + assertEquals(3, vehicle.getDoorCount()); + assertEquals("40 ft", vehicle.getDoorWidth()); + assertEquals(0, vehicle.getLowFloor()); + assertEquals(65, vehicle.getSeatedCapacity()); + assertEquals(40, vehicle.getStandingCapacity()); + assertEquals("no", vehicle.getWheelchairAccess()); + + // Icon for larger vehicle (many to one relationship) + icon = vehicle.getIcon(); + assertEquals(aid("ICO"), icon.getId()); + assertEquals("test icon", icon.getDescription()); + assertEquals("https://iconurl", icon.getUrl()); + } + + private AgencyAndId aid(String id) { + return new AgencyAndId(_agencyId, id); + } +} + diff --git a/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt new file mode 100644 index 000000000..31e2a82e4 --- /dev/null +++ b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/agency.txt @@ -0,0 +1,2 @@ +agency_id,agency_name,agency_url,agency_timezone +agency,Fake Agency,http://fake.example.com,America/New_York diff --git a/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt new file mode 100644 index 000000000..7a679e8ab --- /dev/null +++ b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/icons.txt @@ -0,0 +1,3 @@ +icon_id,icon_description,icon_url +ICO,test icon,https://iconurl + diff --git a/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt new file mode 100644 index 000000000..e119eb1f6 --- /dev/null +++ b/onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/testagency-vehicles-ext/vehicles.txt @@ -0,0 +1,3 @@ +vehicle_id,icon_id,bike_capacity,vehicle_description,door_count,door_width,low_floor,seated_capacity,standing_capacity,wheelchair_access +123,ICO,2,vehicle description,2,30 ft,0,40,20,yes +456,ICO,4,larger vehicle description,3,40 ft,0,65,40,no \ No newline at end of file From 0daf22175aa2ba6eef9d090f5fcf2c5423f596ce Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Tue, 11 Jun 2024 07:28:57 -0400 Subject: [PATCH 026/234] another bug fix on MOTP-2144 --- .../impl/UpdateCalendarDatesForDuplicateTrips.java | 1 + 1 file changed, 1 insertion(+) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java index cfdedf459..6e3259d30 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java @@ -117,6 +117,7 @@ private void deDuplicate(GtfsMutableRelationalDao dao, DuplicateState state, Str private void deDuplicateTrip(GtfsMutableRelationalDao dao, DuplicateState state, Trip exemplar, List tripsToRemove) { Set serviceIds = tripsToRemove.stream().map(l->l.getServiceId()).collect(Collectors.toSet()); + serviceIds.add(exemplar.getServiceId()); addServiceForTrip(dao, state, exemplar, serviceIds); deleteTrips(dao, state, tripsToRemove); } From e49e80776317be03c4cd0edf310d0edc5d5d5f37 Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Tue, 11 Jun 2024 11:01:28 -0400 Subject: [PATCH 027/234] testing temp fix on MOTP-2144 --- .../impl/UpdateCalendarDatesForDuplicateTrips.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java index 6e3259d30..cd2475f3c 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java @@ -76,8 +76,10 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { private void update(GtfsMutableRelationalDao dao, DuplicateState state, HashMap> tripsByMtaTripId) { for (Map.Entry> entry : tripsByMtaTripId.entrySet()) { - update(state, entry.getKey(), entry.getValue()); - deDuplicate(dao, state, entry.getKey(), entry.getValue()); + String mtaTripId = entry.getKey(); + ArrayList atisTrips = entry.getValue(); + update(state, mtaTripId, atisTrips); + deDuplicate(dao, state, mtaTripId, atisTrips); } } @@ -255,8 +257,8 @@ private void applyNewTripIds() { incDuplicateCount(); toModify.setId(mtaTripId); } else { - // we've pruned it to be unique, remove the dup tagging - toModify.setId(removeTag(mtaTripId)); + // TODO we don't think this is a duplicate so removeTag(...) + toModify.setId(mtaTripId); } } else { // the trip has already been deleted, nothing to do From 7f646485b65e88fc2dc8edace990eb9dcc87386d Mon Sep 17 00:00:00 2001 From: sheldonabrown Date: Tue, 11 Jun 2024 19:12:54 -0400 Subject: [PATCH 028/234] reverting previous temp fix --- .../impl/UpdateCalendarDatesForDuplicateTrips.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java index cd2475f3c..c15b564d1 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateCalendarDatesForDuplicateTrips.java @@ -257,9 +257,7 @@ private void applyNewTripIds() { incDuplicateCount(); toModify.setId(mtaTripId); } else { - // TODO we don't think this is a duplicate so removeTag(...) - toModify.setId(mtaTripId); - } + toModify.setId(removeTag(mtaTripId)); } } else { // the trip has already been deleted, nothing to do System.out.println("non-existent trip " + atisTripId + "/" + mtaTripId); From d758989901a46b08ea3071ccf416a9f53faf753d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:30:07 +0000 Subject: [PATCH 029/234] Bump org.json:json in /onebusaway-gtfs-transformer Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20090211 to 20231013. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index c3e51749b..62f674275 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -25,7 +25,7 @@ org.json json - 20090211 + 20231013 junit From 715d95c89624a3b1bf2885aef7333036f8eecfd6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 27 Aug 2024 21:58:20 +0200 Subject: [PATCH 030/234] Reformat pom --- pom.xml | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 1948a2e58..1faec889d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.onebusaway @@ -83,28 +84,28 @@ org.onebusaway onebusaway-csv-entities ${onebusaway_csv_entities_version} - - - slf4j-api - org.slf4j - - + + + slf4j-api + org.slf4j + + org.onebusaway onebusaway-collections ${onebusaway_collections_version} - - org.slf4j - slf4j - ${slf4j_version} - - - org.slf4j - slf4j-api - ${slf4j_version} - + + org.slf4j + slf4j + ${slf4j_version} + + + org.slf4j + slf4j-api + ${slf4j_version} + org.slf4j slf4j-simple From 70a379c0121ca72847f50dab403f6c8293686b9b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 27 Aug 2024 22:04:24 +0200 Subject: [PATCH 031/234] Migrate to Junit 5's vintage engine --- onebusaway-gtfs-hibernate/pom.xml | 9 +++++++-- onebusaway-gtfs-merge/pom.xml | 9 +++++++-- onebusaway-gtfs-transformer-cli-aws/pom.xml | 6 +++--- onebusaway-gtfs-transformer-cli/pom.xml | 11 ++++++++--- onebusaway-gtfs-transformer/pom.xml | 9 +++++++-- onebusaway-gtfs/pom.xml | 4 ++-- pom.xml | 12 +++++++++--- 7 files changed, 43 insertions(+), 17 deletions(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index d82f6cfd6..04c4a3039 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -37,8 +37,13 @@ 8.2.0 - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.vintage + junit-vintage-engine test diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index e51cdf411..80b394a85 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -28,8 +28,13 @@ ${slf4j_version} - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.vintage + junit-vintage-engine test diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index ab549e652..124a7a9f5 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -39,10 +39,10 @@ slf4j-simple ${slf4j_version} - - junit - junit + org.junit.vintage + junit-vintage-engine + test diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index a5d1a85bd..becf8e614 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -34,10 +34,15 @@ slf4j-simple ${slf4j_version} - - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.vintage + junit-vintage-engine + test diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 62f674275..41411f0f1 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -28,8 +28,13 @@ 20231013 - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.vintage + junit-vintage-engine test diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 05111d3a8..e17e61042 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -44,8 +44,8 @@ ${slf4j_version} - junit - junit + org.junit.vintage + junit-vintage-engine test diff --git a/pom.xml b/pom.xml index 1faec889d..fa1e18c9c 100644 --- a/pom.xml +++ b/pom.xml @@ -112,9 +112,15 @@ ${slf4j_version} - junit - junit - 4.13.1 + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.vintage + junit-vintage-engine + 5.6.2 test From 83a4caf1fe28cec71b2045fa744bd7143c844380 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 27 Aug 2024 22:43:39 +0200 Subject: [PATCH 032/234] Migrate to Junit 5 --- onebusaway-gtfs-hibernate/pom.xml | 5 --- .../onebusaway/gtfs/impl/GtfsMappingTest.java | 14 ++++---- ...nateGtfsRelationalDaoImplCaltrainTest.java | 20 +++++------ .../HibernateGtfsRelationalDaoImplTest.java | 14 ++++---- .../HibernateGtfsRelationalImplBartTest.java | 14 ++++---- .../gtfs/impl/LongRouteDescriptionTest.java | 12 +++---- .../org/onebusaway/gtfs/impl/VehicleTest.java | 12 +++---- onebusaway-gtfs-merge/pom.xml | 5 --- .../onebusaway/gtfs_merge/GtfsMergerTest.java | 25 ++++++------- .../gtfs_merge/MergeExpectedFilesTest.java | 16 ++++----- .../strategies/AgencyMergeStrategyTest.java | 10 +++--- .../strategies/EntityMergeTestSupport.java | 4 +-- .../strategies/IntegrationTest.java | 2 +- .../strategies/TripMergeStrategyTest.java | 20 ++--------- .../scoring/DuplicateScoringSupportTest.java | 4 +-- onebusaway-gtfs-transformer-cli-aws/pom.xml | 4 +-- onebusaway-gtfs-transformer-cli/pom.xml | 5 --- onebusaway-gtfs-transformer/pom.xml | 11 +++--- .../gtfs_transformer/AbstractTestSupport.java | 8 ++--- .../gtfs_transformer/AddEntityTest.java | 6 ++-- .../gtfs_transformer/GtfsTransformerTest.java | 12 +++---- .../deferred/DeferredValueConverterTest.java | 8 ++--- .../deferred/DeferredValueMatcherTest.java | 10 +++--- .../deferred/DeferredValueSetterTest.java | 8 ++--- .../deferred/DeferredValueSupportTest.java | 10 +++--- .../deferred/EntitySchemaCacheTest.java | 4 +-- ...PropertyPathExpressionValueSetterTest.java | 8 ++--- .../deferred/ReplaceValueSetterTest.java | 4 +-- .../PropertyMethodResolverImplTest.java | 10 +++--- .../factory/TransformFactoryTest.java | 8 ++--- .../impl/AddExtensionFileTest.java | 17 +++++---- .../CalendarExtensionStrategyTest.java | 12 +++---- .../CalendarSimplicationLibraryTest.java | 4 +-- .../CalendarSimplicationStrategyTest.java | 10 +++--- .../CarryForwardExpectedFilesTest.java | 16 ++++----- .../DeduplicateServiceIdsStrategyTest.java | 12 +++---- .../updates/EntityRetentionGraphTest.java | 14 ++++---- ...teStopTimesFromTimePointsStrategyTest.java | 8 ++--- .../LastStopToHeadsignStrategyTest.java | 15 ++++---- .../MTAStationAccessibilityStrategyTest.java | 21 ++++++----- .../RemoveNonRevenueStopsStrategyTest.java | 8 ++--- .../RemoveRepeatedStopTimesStrategyTest.java | 11 +++--- .../ShapeDirectionTransformStrategyTest.java | 10 +++--- ...ftNegativeStopTimesUpdateStrategyTest.java | 8 ++--- .../TrimTripTransformStrategyTest.java | 8 ++--- ...romParentStationIfInvalidStrategyTest.java | 11 +++--- onebusaway-gtfs/pom.xml | 8 +++-- .../org/onebusaway/gtfs/ExtensionsTest.java | 12 +++---- .../gtfs/impl/GenericDaoImplTest.java | 8 ++--- .../onebusaway/gtfs/impl/GtfsDaoImplTest.java | 4 +-- .../gtfs/impl/GtfsRelationalDaoImplTest.java | 6 ++-- ...arServiceDataFactoryImplSyntheticTest.java | 10 +++--- .../CalendarServiceDataFactoryImplTest.java | 6 ++-- .../CalendarServiceImplSyntheticTest.java | 14 ++++---- .../calendar/CalendarServiceImplTest.java | 6 ++-- .../impl/fares/FareAtrributeAgencyTest.java | 2 +- .../TranslationServiceImplTest.java | 4 +-- .../gtfs/model/AgencyAndIdTest.java | 6 ++-- .../gtfs/model/MissingValueTest.java | 36 +++++++++---------- .../model/StopTimeWithUnderscoreTest.java | 16 ++++----- .../calendar/LocalizedServiceIdTest.java | 6 ++-- .../gtfs/model/calendar/ServiceDateTest.java | 9 +++-- .../calendar/ServiceIdIntervalsTest.java | 4 +-- .../gtfs/serialization/FaresV2ReaderTest.java | 13 +++---- .../FlexDropOffSpellingTest.java | 4 +-- .../gtfs/serialization/FlexReaderTest.java | 8 ++--- .../GtfsReaderDefaultAgencyIdTest.java | 2 +- .../serialization/GtfsReaderStopsTest.java | 16 ++++----- .../gtfs/serialization/GtfsReaderTest.java | 21 +++++------ .../gtfs/serialization/GtfsWriterTest.java | 12 +++---- .../LocationsGeoJSONReaderTest.java | 6 ++-- .../serialization/VehiclesExtReaderTest.java | 6 ++-- .../LatLonFieldMappingFactoryTest.java | 8 ++--- .../ServiceDateFieldMappingFactoryTest.java | 4 +-- .../StopTimeFieldMappingFactoryTest.java | 6 ++-- .../gtfs/services/MockGtfsTest.java | 8 ++--- pom.xml | 24 +++++++++---- 77 files changed, 374 insertions(+), 399 deletions(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 04c4a3039..55bcf42e7 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -41,11 +41,6 @@ junit-jupiter-api test - - org.junit.vintage - junit-vintage-engine - test - org.slf4j slf4j-simple diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java index d7cd1cb65..4ae4443bf 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java @@ -15,17 +15,17 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.io.StringReader; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; @@ -43,7 +43,7 @@ public class GtfsMappingTest { private static GtfsReader _reader; - @Before + @BeforeEach public void setup() throws IOException { Configuration config = new Configuration(); @@ -57,7 +57,7 @@ public void setup() throws IOException { _reader.setEntityStore(_dao); } - @After + @AfterEach public void teardown() { if (_dao != null) _dao.close(); diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java index 365f7bbe4..7ef7313fb 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java @@ -16,11 +16,11 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -30,9 +30,9 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Route; @@ -53,7 +53,7 @@ public class HibernateGtfsRelationalDaoImplCaltrainTest { private static HibernateGtfsRelationalDaoImpl _dao; - @BeforeClass + @BeforeAll public static void setup() throws IOException { Configuration config = new Configuration(); @@ -70,7 +70,7 @@ public static void setup() throws IOException { reader.run(); } - @AfterClass + @AfterAll public static void teardown() { _sessionFactory.close(); } diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplTest.java index c2fbbd614..9d328797a 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.HashSet; @@ -25,9 +25,9 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Stop; @@ -37,7 +37,7 @@ public class HibernateGtfsRelationalDaoImplTest { private static HibernateGtfsRelationalDaoImpl _dao; - @Before + @BeforeEach public void setup() throws IOException { Configuration config = new Configuration(); @@ -48,7 +48,7 @@ public void setup() throws IOException { _dao.open(); } - @After + @AfterEach public void teardown() { if (_dao != null) _dao.close(); diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalImplBartTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalImplBartTest.java index 9ed08d7b9..85879865e 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalImplBartTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalImplBartTest.java @@ -16,8 +16,8 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -25,9 +25,9 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Frequency; import org.onebusaway.gtfs.model.ServiceCalendar; @@ -44,7 +44,7 @@ public class HibernateGtfsRelationalImplBartTest { private static HibernateGtfsRelationalDaoImpl _dao; - @BeforeClass + @BeforeAll public static void setup() throws IOException { Configuration config = new Configuration(); @@ -61,7 +61,7 @@ public static void setup() throws IOException { reader.run(); } - @AfterClass + @AfterAll public static void teardown() { _sessionFactory.close(); } diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/LongRouteDescriptionTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/LongRouteDescriptionTest.java index 4b45b0620..8ec801906 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/LongRouteDescriptionTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/LongRouteDescriptionTest.java @@ -15,7 +15,7 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; @@ -23,9 +23,9 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Route; @@ -39,7 +39,7 @@ public class LongRouteDescriptionTest { private static HibernateGtfsRelationalDaoImpl _dao; - @BeforeClass + @BeforeAll public static void setup() throws IOException { Configuration config = new Configuration(); @@ -62,7 +62,7 @@ public static void setup() throws IOException { reader.run(); } - @AfterClass + @AfterAll public static void teardown() { _sessionFactory.close(); } diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java index 5693a32a9..5730ed4db 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/VehicleTest.java @@ -17,9 +17,9 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.*; import org.onebusaway.gtfs.serialization.GtfsReader; @@ -27,7 +27,7 @@ import java.io.IOException; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class VehicleTest { private static SessionFactory _sessionFactory; @@ -36,7 +36,7 @@ public class VehicleTest { private static HibernateGtfsRelationalDaoImpl _dao; - @BeforeClass + @BeforeAll public static void setup() throws IOException { Configuration config = new Configuration(); @@ -59,7 +59,7 @@ public static void setup() throws IOException { reader.run(); } - @AfterClass + @AfterAll public static void teardown() { _sessionFactory.close(); } diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 80b394a85..6bfe28927 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -32,11 +32,6 @@ junit-jupiter-api test - - org.junit.vintage - junit-vintage-engine - test - org.mockito mockito-core diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java index aafe73725..2344f6e5c 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/GtfsMergerTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs_merge; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -24,9 +24,9 @@ import java.util.Iterator; import java.util.List; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.*; import org.onebusaway.gtfs.serialization.GtfsReader; @@ -56,7 +56,7 @@ public class GtfsMergerTest { private GtfsMerger _merger; - @Before + @BeforeEach public void before() throws IOException { _oldGtfs = MockGtfs.create(); _newGtfs = MockGtfs.create(); @@ -65,7 +65,7 @@ public void before() throws IOException { _merger = new GtfsMerger(); } - @After + @AfterEach public void after() { } @@ -224,7 +224,7 @@ public void testAgencyPreference() throws IOException { pugetStopFound = true; } } - assertTrue("expect a puget stop", pugetStopFound); + assertTrue(pugetStopFound, "expect a puget stop"); } @Test @@ -352,7 +352,7 @@ public void testRenameStrategy() throws IOException { assertTrue("b-sid0".matches("[a-j]-.*")); - assertTrue("expect a puget stop", pugetStopFound); + assertTrue(pugetStopFound, "expect a puget stop"); } // tests stop, location, and location group @@ -454,10 +454,11 @@ public void testStopTimeProxies() throws IOException { boolean hasST = st.getStop()!=null; boolean hasLoc = st.getLocation()!=null; boolean hasLocGroup = st.getLocationGroup()!=null; - assertTrue("multiple ids found for stop: "+st.getStop()+ + assertTrue( !(hasST & hasLoc | hasST && hasLocGroup | hasLoc & hasLocGroup), + "multiple ids found for stop: "+st.getStop()+ ", location_id: "+st.getLocation()+ - ", location_id: "+st.getLocationGroup(), - !(hasST & hasLoc | hasST && hasLocGroup | hasLoc & hasLocGroup)); + ", location_id: "+st.getLocationGroup() + ); } } diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/MergeExpectedFilesTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/MergeExpectedFilesTest.java index 02be0698d..9246dda1f 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/MergeExpectedFilesTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/MergeExpectedFilesTest.java @@ -15,9 +15,9 @@ */ package org.onebusaway.gtfs_merge; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.FileSupport; import org.onebusaway.gtfs.impl.ZipHandler; @@ -29,7 +29,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test appending metadata inputs as part of merge. @@ -40,12 +40,12 @@ public class MergeExpectedFilesTest { private FileSupport _support = new FileSupport(); - @Before + @BeforeEach public void before() throws IOException { _merger = new GtfsMerger(); } - @After + @AfterEach public void after() { _support.cleanup(); } @@ -68,8 +68,8 @@ public void testDirectoryMerge() throws Exception { String modLocation = gtfsDirectory.getAbsolutePath() + File.separator + "modifications.txt"; File expectedFile = new File(modLocation); // verify modifications.txt is there!!!! - assertTrue("expected modifications.txt to be present!", expectedFile.exists()); - assertTrue("expected modifications.txt to be a file!", expectedFile.isFile()); + assertTrue(expectedFile.exists(), "expected modifications.txt to be present!"); + assertTrue(expectedFile.isFile(), "expected modifications.txt to be a file!"); StringBuffer sb = new StringBuffer(); BufferedReader br = new BufferedReader(new FileReader(expectedFile)); sb.append(br.lines().collect(Collectors.joining(System.lineSeparator()))); diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/AgencyMergeStrategyTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/AgencyMergeStrategyTest.java index 621f20889..23e793845 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/AgencyMergeStrategyTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/AgencyMergeStrategyTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_merge.strategies; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.util.Collection; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; @@ -40,7 +40,7 @@ public class AgencyMergeStrategyTest extends EntityMergeTestSupport { private GtfsRelationalDaoImpl _target; - @Before + @BeforeEach public void before() { _strategy = new AgencyMergeStrategy(); _target = new GtfsRelationalDaoImpl(); diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/EntityMergeTestSupport.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/EntityMergeTestSupport.java index d1572930f..af128f1f6 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/EntityMergeTestSupport.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/EntityMergeTestSupport.java @@ -18,7 +18,7 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs_merge.GtfsMergeContext; @@ -26,7 +26,7 @@ public class EntityMergeTestSupport { protected Map entityByRawId; - @Before + @BeforeEach public void setup() { entityByRawId = new HashMap(); } diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/IntegrationTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/IntegrationTest.java index fe37d8271..e6655f291 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/IntegrationTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/IntegrationTest.java @@ -17,7 +17,7 @@ import java.io.IOException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.services.MockGtfs; public class IntegrationTest { diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/TripMergeStrategyTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/TripMergeStrategyTest.java index 5afe3a801..e1e10013a 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/TripMergeStrategyTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/TripMergeStrategyTest.java @@ -15,24 +15,8 @@ */ package org.onebusaway.gtfs_merge.strategies; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -import java.util.Collection; - -import org.junit.Before; -import org.junit.Test; -import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; -import org.onebusaway.gtfs.model.Agency; -import org.onebusaway.gtfs.model.AgencyAndId; -import org.onebusaway.gtfs.model.FareAttribute; -import org.onebusaway.gtfs.model.Route; -import org.onebusaway.gtfs.model.ServiceCalendar; -import org.onebusaway.gtfs.model.ServiceCalendarDate; -import org.onebusaway.gtfs.model.ShapePoint; -import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.Trip; -import org.onebusaway.gtfs_merge.GtfsMergeContext; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; public class TripMergeStrategyTest extends EntityMergeTestSupport { diff --git a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/scoring/DuplicateScoringSupportTest.java b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/scoring/DuplicateScoringSupportTest.java index e6e3008e2..63e0cb2b7 100644 --- a/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/scoring/DuplicateScoringSupportTest.java +++ b/onebusaway-gtfs-merge/src/test/java/org/onebusaway/gtfs_merge/strategies/scoring/DuplicateScoringSupportTest.java @@ -15,12 +15,12 @@ */ package org.onebusaway.gtfs_merge.strategies.scoring; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashSet; import java.util.Set; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class DuplicateScoringSupportTest { diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml index 124a7a9f5..8975ed344 100644 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ b/onebusaway-gtfs-transformer-cli-aws/pom.xml @@ -40,8 +40,8 @@ ${slf4j_version} - org.junit.vintage - junit-vintage-engine + org.junit.jupiter + junit-jupiter-api test diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index becf8e614..7fa6d376f 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -39,11 +39,6 @@ junit-jupiter-api test - - org.junit.vintage - junit-vintage-engine - test - diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 41411f0f1..44356ec57 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -21,6 +21,12 @@ org.onebusaway onebusaway-collections + + + junit + junit + + org.json @@ -31,11 +37,6 @@ org.junit.jupiter junit-jupiter-api test - - - org.junit.vintage - junit-vintage-engine - test org.slf4j diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AbstractTestSupport.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AbstractTestSupport.java index d42fa54b7..d67fe9020 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AbstractTestSupport.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AbstractTestSupport.java @@ -18,8 +18,8 @@ import java.io.File; import java.io.IOException; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.serialization.GtfsReader; import org.onebusaway.gtfs.services.GtfsRelationalDao; @@ -34,7 +34,7 @@ public class AbstractTestSupport { private File _outputPath; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); _outputPath = File.createTempFile("MockGtfs-", ".zip"); @@ -45,7 +45,7 @@ public void before() throws IOException { _transformer.setOutputDirectory(_outputPath); } - @After + @AfterEach public void after() { _gtfs.getPath().delete(); _outputPath.delete(); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AddEntityTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AddEntityTest.java index 2f0d603d0..fd67b3c7b 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AddEntityTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/AddEntityTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_transformer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.util.Collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Frequency; import org.onebusaway.gtfs.serialization.mappings.StopTimeFieldMappingFactory; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/GtfsTransformerTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/GtfsTransformerTest.java index ea79c68dc..3971675b0 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/GtfsTransformerTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/GtfsTransformerTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs_transformer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +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.io.IOException; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.services.GtfsRelationalDao; @@ -35,7 +35,7 @@ public class GtfsTransformerTest { private GtfsTransformer _transformer = new GtfsTransformer(); - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); _gtfs.putAgencies(1); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueConverterTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueConverterTest.java index b84ade38d..f7a150a50 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueConverterTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueConverterTest.java @@ -15,10 +15,10 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; @@ -43,7 +43,7 @@ public class DeferredValueConverterTest { private DeferredValueConverter _converter; - @Before + @BeforeEach public void setup() { _schemaCache.addEntitySchemasFromGtfsReader(_reader); _reader.setDefaultAgencyId("1"); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueMatcherTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueMatcherTest.java index 499fdc498..006f40bb0 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueMatcherTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueMatcherTest.java @@ -15,11 +15,11 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Route; import org.onebusaway.gtfs.model.ServiceCalendar; @@ -35,7 +35,7 @@ public class DeferredValueMatcherTest { private EntitySchemaCache _schemaCache = new EntitySchemaCache(); - @Before + @BeforeEach public void setup() { _reader.setDefaultAgencyId("1"); _schemaCache.addEntitySchemasFromGtfsReader(_reader); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSetterTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSetterTest.java index 5f83adbce..b0715b78c 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSetterTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSetterTest.java @@ -15,10 +15,10 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Stop; @@ -33,7 +33,7 @@ public class DeferredValueSetterTest { private EntitySchemaCache _schemaCache = new EntitySchemaCache(); - @Before + @BeforeEach public void setup() { _schemaCache.addEntitySchemasFromGtfsReader(_reader); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSupportTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSupportTest.java index 4c81da8bc..b1fcd860d 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSupportTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/DeferredValueSupportTest.java @@ -15,15 +15,15 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.BeanWrapper; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.csv_entities.schema.EntitySchema; @@ -41,7 +41,7 @@ public class DeferredValueSupportTest { private EntitySchemaCache _schemaCache; private DeferredValueSupport _support; - @Before + @BeforeEach public void before() { _reader = new GtfsReader(); _reader.setDefaultAgencyId("a0"); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/EntitySchemaCacheTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/EntitySchemaCacheTest.java index 23d159fc8..36ba44d8e 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/EntitySchemaCacheTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/EntitySchemaCacheTest.java @@ -15,9 +15,9 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertSame; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.DefaultFieldMapping; import org.onebusaway.csv_entities.schema.EntitySchema; import org.onebusaway.csv_entities.schema.FieldMapping; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/PropertyPathExpressionValueSetterTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/PropertyPathExpressionValueSetterTest.java index dd59f0006..9e6a497b6 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/PropertyPathExpressionValueSetterTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/PropertyPathExpressionValueSetterTest.java @@ -15,10 +15,10 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.collections.beans.PropertyPathExpression; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; @@ -36,7 +36,7 @@ public class PropertyPathExpressionValueSetterTest { private EntitySchemaCache _schemaCache = new EntitySchemaCache(); private GtfsMutableRelationalDao _dao = new GtfsRelationalDaoImpl(); - @Before + @BeforeEach public void setup() { _schemaCache.addEntitySchemasFromGtfsReader(_reader); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/ReplaceValueSetterTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/ReplaceValueSetterTest.java index cb6c34ef5..abb963321 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/ReplaceValueSetterTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/deferred/ReplaceValueSetterTest.java @@ -15,9 +15,9 @@ */ package org.onebusaway.gtfs_transformer.deferred; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.gtfs.model.Stop; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/PropertyMethodResolverImplTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/PropertyMethodResolverImplTest.java index 139543138..1d3035f06 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/PropertyMethodResolverImplTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/PropertyMethodResolverImplTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs_transformer.factory; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.collections.beans.PropertyMethod; import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; import org.onebusaway.csv_entities.schema.EntitySchema; @@ -46,7 +46,7 @@ public class PropertyMethodResolverImplTest { private PropertyMethodResolverImpl _resolver; - @Before + @BeforeEach public void before() { _dao = new GtfsRelationalDaoImpl(); _schemaCache = new EntitySchemaCache(); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/TransformFactoryTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/TransformFactoryTest.java index 0c8147f61..977000045 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/TransformFactoryTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/factory/TransformFactoryTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs_transformer.factory; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Route; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/AddExtensionFileTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/AddExtensionFileTest.java index c4e816503..600ee88ae 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/AddExtensionFileTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/AddExtensionFileTest.java @@ -15,13 +15,12 @@ */ package org.onebusaway.gtfs_transformer.impl; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.FileSupport; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.serialization.GtfsWriter; -import org.onebusaway.gtfs.services.GtfsRelationalDao; import org.onebusaway.gtfs_transformer.AbstractTestSupport; import org.onebusaway.gtfs_transformer.services.TransformContext; @@ -32,7 +31,7 @@ import java.nio.file.Files; import java.nio.file.Path; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * test we can insert an extension into a GTFS file via a transformation. @@ -46,7 +45,7 @@ public class AddExtensionFileTest extends AbstractTestSupport { private TransformContext context = new TransformContext(); private GtfsWriter writer = new GtfsWriter(); - @Before + @BeforeEach public void setup() { _gtfs.putAgencies(1); _gtfs.putStops(1); @@ -55,7 +54,7 @@ public void setup() { _gtfs.putStopTimes("t0", "s0"); } - @After + @AfterEach public void teardown() { _support.cleanup(); } @@ -89,8 +88,8 @@ public void run() throws IOException { String modLocation = tmpFileDirectory.getAbsolutePath() + File.separator + extensionName; File expectedFile = new File(modLocation); // verify file is there - assertTrue("expected extension to be present!", expectedFile.exists()); - assertTrue("expected extension to be a file!", expectedFile.isFile()); + assertTrue(expectedFile.exists(), "expected extension to be present!"); + assertTrue(expectedFile.isFile(), "expected extension to be a file!"); String actualText = Files.readString(Path.of(modLocation)); assertEquals(TXT_STRING, actualText); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarExtensionStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarExtensionStrategyTest.java index b771193dc..ab9de5526 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarExtensionStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarExtensionStrategyTest.java @@ -15,15 +15,15 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Set; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.calendar.CalendarServiceDataFactoryImpl; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ServiceCalendar; @@ -41,7 +41,7 @@ public class CalendarExtensionStrategyTest { private TransformContext _context = new TransformContext(); - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); _gtfs.putAgencies(1); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationLibraryTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationLibraryTest.java index 237562a3b..4a012007c 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationLibraryTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationLibraryTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.calendar.CalendarServiceDataFactoryImpl; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ServiceCalendar; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationStrategyTest.java index 652b75526..8c79cf5cc 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationStrategyTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ServiceCalendar; import org.onebusaway.gtfs.model.ServiceCalendarDate; @@ -31,7 +31,7 @@ public class CalendarSimplicationStrategyTest extends AbstractTestSupport { - @Before + @BeforeEach public void setup() { _transformer.addTransform(new CalendarSimplicationStrategy()); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CarryForwardExpectedFilesTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CarryForwardExpectedFilesTest.java index 8fcef1bcd..94be17e4b 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CarryForwardExpectedFilesTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/CarryForwardExpectedFilesTest.java @@ -15,9 +15,9 @@ */ package org.onebusaway.gtfs_transformer.updates; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.FileSupport; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.impl.ZipHandler; @@ -28,7 +28,7 @@ import java.io.IOException; import java.net.URISyntaxException; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Ensure metadata files are still present in typical read/write cycle. @@ -42,12 +42,12 @@ public class CarryForwardExpectedFilesTest { private FileSupport _support = new FileSupport(); - @Before + @BeforeEach public void setup() throws IOException, URISyntaxException { _dao = new GtfsRelationalDaoImpl(); } - @After + @AfterEach public void teardown() { _support.cleanup(); } @@ -75,8 +75,8 @@ public void testFile() throws Exception { String modLocation = _tmpFileDirectory.getAbsolutePath() + File.separator + "modifications.txt"; File expectedFile = new File(modLocation); // verify modifications.txt is there!!!! - assertTrue("expected modifications.txt to be present!", expectedFile.exists()); - assertTrue("expected modifications.txt to be a file!", expectedFile.isFile()); + assertTrue(expectedFile.exists(), "expected modifications.txt to be present!"); + assertTrue(expectedFile.isFile(), "expected modifications.txt to be a file!"); } @Test diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/DeduplicateServiceIdsStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/DeduplicateServiceIdsStrategyTest.java index 2e70aecda..822cbf469 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/DeduplicateServiceIdsStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/DeduplicateServiceIdsStrategyTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +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.io.IOException; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; @@ -37,7 +37,7 @@ public class DeduplicateServiceIdsStrategyTest { private TransformContext _context = new TransformContext(); - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); _gtfs.putAgencies(1); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/EntityRetentionGraphTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/EntityRetentionGraphTest.java index c877c8a89..9475b32b8 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/EntityRetentionGraphTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/EntityRetentionGraphTest.java @@ -15,24 +15,22 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.AgencyAndId; -import org.onebusaway.gtfs.model.FareRule; import org.onebusaway.gtfs.model.ShapePoint; import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.serialization.GtfsReader; -import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs.services.GtfsRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; import org.onebusaway.gtfs_transformer.factory.EntityRetentionGraph; @@ -43,7 +41,7 @@ public class EntityRetentionGraphTest { private EntityRetentionGraph _graph; - @Before + @BeforeEach public void setup() throws IOException, URISyntaxException { GtfsRelationalDaoImpl dao = new GtfsRelationalDaoImpl(); _dao = dao; diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/InterpolateStopTimesFromTimePointsStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/InterpolateStopTimesFromTimePointsStrategyTest.java index 7c923eef2..d041c82da 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/InterpolateStopTimesFromTimePointsStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/InterpolateStopTimesFromTimePointsStrategyTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs_transformer.updates; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; @@ -30,7 +30,7 @@ import java.util.Date; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * Sanity check the interpolation strategy. @@ -44,7 +44,7 @@ public class InterpolateStopTimesFromTimePointsStrategyTest { private TransformContext _context = new TransformContext(); - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/LastStopToHeadsignStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/LastStopToHeadsignStrategyTest.java index cc255b71f..45eb4c038 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/LastStopToHeadsignStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/LastStopToHeadsignStrategyTest.java @@ -16,9 +16,12 @@ package org.onebusaway.gtfs_transformer.updates; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Trip; @@ -32,7 +35,7 @@ public class LastStopToHeadsignStrategyTest { private GtfsRelationalDaoImpl _dao; - @Before + @BeforeEach public void setup() throws IOException, URISyntaxException { _dao = new GtfsRelationalDaoImpl(); @@ -53,10 +56,10 @@ public void test() { tripId.setAgencyId("agency"); Trip trip = _dao.getTripForId(tripId); - Assert.assertNotSame("C",trip.getTripHeadsign()); + assertNotSame("C",trip.getTripHeadsign()); _strategy.run(new TransformContext(), _dao); trip = _dao.getTripForId(tripId); - Assert.assertEquals("C",trip.getTripHeadsign()); + assertEquals("C",trip.getTripHeadsign()); } } \ No newline at end of file diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java index 176e8eceb..46a436484 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/MTAStationAccessibilityStrategyTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs_transformer.updates; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.services.GtfsRelationalDao; @@ -24,8 +24,8 @@ import java.io.File; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.onebusaway.gtfs_transformer.csv.MTAStation.*; /** @@ -33,7 +33,7 @@ */ public class MTAStationAccessibilityStrategyTest extends AbstractTestSupport { - @Before + @BeforeEach public void setup() throws Exception { File stations_csv = new File(getClass().getResource( "/org/onebusaway/gtfs_transformer/stations/stations.csv").toURI()); @@ -66,17 +66,16 @@ private void assertStation(GtfsRelationalDao dao, String stopId, int ada, Intege Stop sStop = dao.getStopForId(new AgencyAndId("a0", stopId+"S")); assertNotNull(sStop); - assertEquals("expecting ada flag to match wheelchairBoarding flag for stop " + parentStop.getId(), - ada, converGTFSccessibilityToMTA(parentStop.getWheelchairBoarding())); + assertEquals(converGTFSccessibilityToMTA(parentStop.getWheelchairBoarding()), ada, "expecting ada flag to match wheelchairBoarding flag for stop " + parentStop.getId()); if (northBoundFlag == null) { - assertEquals("expecting N/A wheelchairBoarding for northbound stop " + nStop,0, converGTFSccessibilityToMTA(nStop.getWheelchairBoarding())); // default is 0 + assertEquals(0, converGTFSccessibilityToMTA(nStop.getWheelchairBoarding()), "expecting N/A wheelchairBoarding for northbound stop " + nStop); // default is 0 } else { - assertEquals("expecting northBoundFlag to match wheelchairBoarding flag for stop" + nStop, northBoundFlag.intValue(), converGTFSccessibilityToMTA(nStop.getWheelchairBoarding())); + assertEquals(northBoundFlag.intValue(), converGTFSccessibilityToMTA(nStop.getWheelchairBoarding()), "expecting northBoundFlag to match wheelchairBoarding flag for stop" + nStop); } if (southBoundFlag == null) { - assertEquals("expecting N/A wheelchairBoarding for southbound stop " + sStop,0, converGTFSccessibilityToMTA(sStop.getWheelchairBoarding())); + assertEquals(0, converGTFSccessibilityToMTA(sStop.getWheelchairBoarding()), "expecting N/A wheelchairBoarding for southbound stop " + sStop); } else { - assertEquals("expecting southBoundFlag to match wheelchairBoarding flag for stop" + sStop, southBoundFlag.intValue(), converGTFSccessibilityToMTA(sStop.getWheelchairBoarding())); + assertEquals(southBoundFlag.intValue(), converGTFSccessibilityToMTA(sStop.getWheelchairBoarding()), "expecting southBoundFlag to match wheelchairBoarding flag for stop" + sStop); } } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveNonRevenueStopsStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveNonRevenueStopsStrategyTest.java index 7c1f8252b..1ea211742 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveNonRevenueStopsStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveNonRevenueStopsStrategyTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; @@ -33,7 +33,7 @@ public class RemoveNonRevenueStopsStrategyTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveRepeatedStopTimesStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveRepeatedStopTimesStrategyTest.java index c7cf84b9f..de6e363c3 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveRepeatedStopTimesStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/RemoveRepeatedStopTimesStrategyTest.java @@ -15,11 +15,10 @@ */ package org.onebusaway.gtfs_transformer.updates; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.collections.tuple.T2; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; -import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.serialization.GtfsReader; @@ -32,8 +31,8 @@ import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.onebusaway.gtfs_transformer.updates.TripsByBlockInSortedOrder.getTripsByBlockAndServiceIdInSortedOrder; /** @@ -43,7 +42,7 @@ public class RemoveRepeatedStopTimesStrategyTest { private GtfsRelationalDaoImpl _dao; private TransformContext _context = new TransformContext(); - @Before + @BeforeEach public void before() throws IOException { _dao = new GtfsRelationalDaoImpl(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShapeDirectionTransformStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShapeDirectionTransformStrategyTest.java index 4bda9284f..86b31085b 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShapeDirectionTransformStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShapeDirectionTransformStrategyTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ShapePoint; @@ -25,8 +25,8 @@ import org.onebusaway.gtfs.services.MockGtfs; import org.onebusaway.gtfs_transformer.services.TransformContext; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Collection; @@ -41,7 +41,7 @@ public class ShapeDirectionTransformStrategyTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShiftNegativeStopTimesUpdateStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShiftNegativeStopTimesUpdateStrategyTest.java index cd659acba..33e81b945 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShiftNegativeStopTimesUpdateStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/ShiftNegativeStopTimesUpdateStrategyTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.ServiceCalendar; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.model.Trip; @@ -37,7 +37,7 @@ public class ShiftNegativeStopTimesUpdateStrategyTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/TrimTripTransformStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/TrimTripTransformStrategyTest.java index 161eef65f..7c090237c 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/TrimTripTransformStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/TrimTripTransformStrategyTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs_transformer.updates; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.Collection; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.collections.beans.PropertyPathExpression; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ShapePoint; @@ -45,7 +45,7 @@ public class TrimTripTransformStrategyTest { private TransformContext _context = new TransformContext(); - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/UpdateStopNameFromParentStationIfInvalidStrategyTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/UpdateStopNameFromParentStationIfInvalidStrategyTest.java index 6641e0f57..ae485faeb 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/UpdateStopNameFromParentStationIfInvalidStrategyTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/updates/UpdateStopNameFromParentStationIfInvalidStrategyTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs_transformer.updates; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; @@ -25,18 +25,19 @@ import java.io.IOException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class UpdateStopNameFromParentStationIfInvalidStrategyTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); } - @Test public void test() throws IOException { + @Test + public void test() throws IOException { _gtfs.putAgencies(1); _gtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon,location_type,parent_station,platform_code", diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index e17e61042..c3c06fe85 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 onebusaway-gtfs jar @@ -44,8 +45,8 @@ ${slf4j_version} - org.junit.vintage - junit-vintage-engine + org.junit.jupiter + junit-jupiter-api test @@ -57,6 +58,7 @@ + com.mycila license-maven-plugin diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/ExtensionsTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/ExtensionsTest.java index 48f876420..a0d99da82 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/ExtensionsTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/ExtensionsTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; @@ -39,7 +39,7 @@ public class ExtensionsTest { private File _tmpDirectory; - @Before + @BeforeEach public void setup() throws IOException { _tmpDirectory = File.createTempFile("GtfsWriterTest-", "-tmp"); if (_tmpDirectory.exists()) @@ -47,7 +47,7 @@ public void setup() throws IOException { _tmpDirectory.mkdirs(); } - @After + @AfterEach public void teardown() { GtfsWriterTest.deleteFileRecursively(_tmpDirectory); } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GenericDaoImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GenericDaoImplTest.java index 9f3b88060..8a9b345cc 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GenericDaoImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GenericDaoImplTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import java.util.Collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Stop; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsDaoImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsDaoImplTest.java index 8ffe17973..3176c83c5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsDaoImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsDaoImplTest.java @@ -15,7 +15,7 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Agency; @@ -33,7 +33,7 @@ import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.model.calendar.ServiceDate; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Collection; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsRelationalDaoImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsRelationalDaoImplTest.java index c74c5dd78..1e0c9a850 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsRelationalDaoImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/GtfsRelationalDaoImplTest.java @@ -16,13 +16,13 @@ */ package org.onebusaway.gtfs.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplSyntheticTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplSyntheticTest.java index d1c76b62a..b9f2dc2dc 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplSyntheticTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplSyntheticTest.java @@ -17,10 +17,10 @@ */ package org.onebusaway.gtfs.impl.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; @@ -29,7 +29,7 @@ import java.util.Set; import java.util.TimeZone; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.DateSupport; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplTest.java index d75a08a66..83a61ac6d 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceDataFactoryImplTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs.impl.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Date; @@ -24,7 +24,7 @@ import java.util.Set; import java.util.TimeZone; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.DateSupport; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplSyntheticTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplSyntheticTest.java index cb8294a16..2547ef0a5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplSyntheticTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplSyntheticTest.java @@ -15,17 +15,17 @@ */ package org.onebusaway.gtfs.impl.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.onebusaway.gtfs.DateSupport.date; import static org.onebusaway.gtfs.DateSupport.hourToSec; import java.util.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.calendar.*; @@ -53,7 +53,7 @@ public class CalendarServiceImplSyntheticTest { private CalendarServiceImpl service; - @Before + @BeforeEach public void setup() { CalendarServiceData data = new CalendarServiceData(); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplTest.java index 581310463..f21ade2a6 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/calendar/CalendarServiceImplTest.java @@ -15,8 +15,8 @@ */ package org.onebusaway.gtfs.impl.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Calendar; @@ -24,7 +24,7 @@ import java.util.HashSet; import java.util.Set; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.AgencyAndId; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/fares/FareAtrributeAgencyTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/fares/FareAtrributeAgencyTest.java index 543ed8498..1512983e8 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/fares/FareAtrributeAgencyTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/fares/FareAtrributeAgencyTest.java @@ -15,7 +15,7 @@ */ package org.onebusaway.gtfs.impl.fares; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.FareAttribute; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/translation/TranslationServiceImplTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/translation/TranslationServiceImplTest.java index 98d571571..0529b4bcf 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/translation/TranslationServiceImplTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/impl/translation/TranslationServiceImplTest.java @@ -15,7 +15,7 @@ */ package org.onebusaway.gtfs.impl.translation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; @@ -29,7 +29,7 @@ import java.io.IOException; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TranslationServiceImplTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/AgencyAndIdTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/AgencyAndIdTest.java index 19d4440e8..6e1ae60b5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/AgencyAndIdTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/AgencyAndIdTest.java @@ -15,10 +15,10 @@ */ package org.onebusaway.gtfs.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class AgencyAndIdTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/MissingValueTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/MissingValueTest.java index bbe6f6613..c36111091 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/MissingValueTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/MissingValueTest.java @@ -19,15 +19,15 @@ import java.io.IOException; import java.util.Scanner; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.serialization.GtfsWriter; import org.onebusaway.gtfs.serialization.GtfsWriterTest; import org.onebusaway.gtfs.services.MockGtfs; import org.onebusaway.gtfs.services.GtfsRelationalDao; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * Optional flex fields in stop times should not be present @@ -40,7 +40,7 @@ public class MissingValueTest { private File _tmpDirectory; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); @@ -88,9 +88,9 @@ public void test() throws IOException { foundShapeDist = true; } } - assertFalse("Empty Column not properly removed in line " + headerLine, foundEmptyColumn); - assertTrue("Column unexpectedly removed in line " + headerLine, foundProperArrivalTime); - assertFalse("Not expecting shapeDistTraveled in line " + headerLine, foundShapeDist); + assertFalse(foundEmptyColumn, "Empty Column not properly removed in line " + headerLine); + assertTrue(foundProperArrivalTime, "Column unexpectedly removed in line " + headerLine); + assertFalse(foundShapeDist, "Not expecting shapeDistTraveled in line " + headerLine); } @Test @@ -117,7 +117,7 @@ public void testStartingWithMissingValue() throws IOException { foundTimepoint = true; } } - assertFalse("Empty Column not properly removed", foundTimepoint); + assertFalse(foundTimepoint, "Empty Column not properly removed" ); } /** @@ -182,14 +182,14 @@ public void testStartingWithMissingDoubleValue() throws IOException { } } - assertFalse("Empty Column not properly removed", foundTimepoint); - assertFalse("Not expecting shapeDistTraveled in line " + headerLine, foundShapeDist); - assertFalse("Not expecting start service area radius in line " + headerLine, foundStartServiceArea); - assertFalse("Not expecting end service area radius in line " + headerLine, foundEndServiceArea); - assertFalse("Not expecting mean duration factor in line " + headerLine, foundMeanDurationFactor); - assertFalse("Not expecting mean duration offset in line " + headerLine, foundMeanDurationOffset); - assertFalse("Not expecting safe duration factor in line " + headerLine, foundSafeDurationFactor); - assertFalse("Not expecting safe duration offset in line " + headerLine, foundSafeDurationOffset); + assertFalse(foundTimepoint, "Empty Column not properly removed"); + assertFalse(foundShapeDist, "Not expecting shapeDistTraveled in line " + headerLine); + assertFalse(foundStartServiceArea, "Not expecting start service area radius in line " + headerLine ); + assertFalse(foundEndServiceArea, "Not expecting end service area radius in line " + headerLine ); + assertFalse(foundMeanDurationFactor, "Not expecting mean duration factor in line " + headerLine ); + assertFalse(foundMeanDurationOffset, "Not expecting mean duration offset in line " + headerLine ); + assertFalse(foundSafeDurationFactor, "Not expecting safe duration factor in line " + headerLine ); + assertFalse(foundSafeDurationOffset, "Not expecting safe duration offset in line " + headerLine); } @Test @@ -199,7 +199,7 @@ public void testPutMinimal() throws IOException { _gtfs.read(); } - @After + @AfterEach public void teardown() { deleteFileRecursively(_tmpDirectory); } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java index 9a5240a7e..bdb0112c6 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/StopTimeWithUnderscoreTest.java @@ -19,15 +19,15 @@ import java.io.IOException; import java.util.*; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.serialization.GtfsWriter; import org.onebusaway.gtfs.serialization.GtfsWriterTest; import org.onebusaway.gtfs.services.MockGtfs; import org.onebusaway.gtfs.services.GtfsRelationalDao; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class StopTimeWithUnderscoreTest { @@ -35,7 +35,7 @@ public class StopTimeWithUnderscoreTest { private File _tmpDirectory; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); @@ -71,7 +71,7 @@ public void testWithUnderScore() throws IOException { } } // if the underscore version was input use it as output - assertTrue("Column without underscore was not found", foundUnderscoreParam); + assertTrue(foundUnderscoreParam, "Column without underscore was not found"); } @Test @@ -99,7 +99,7 @@ public void testWithoutUnderscore() throws IOException { } } // if the new spec was used as input ensure it is output - assertTrue("Column without underscore was not found", foundUnderscoreParam); + assertTrue(foundUnderscoreParam, "Column without underscore was not found"); } @Test @@ -109,7 +109,7 @@ public void testPutMinimal() throws IOException { _gtfs.read(); } - @After + @AfterEach public void teardown() { deleteFileRecursively(_tmpDirectory); } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/LocalizedServiceIdTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/LocalizedServiceIdTest.java index d77e7f5a6..ee21d9937 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/LocalizedServiceIdTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/LocalizedServiceIdTest.java @@ -15,12 +15,12 @@ */ package org.onebusaway.gtfs.model.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.TimeZone; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; public class LocalizedServiceIdTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceDateTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceDateTest.java index b7ddd6afd..403db6ad5 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceDateTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceDateTest.java @@ -16,9 +16,9 @@ */ package org.onebusaway.gtfs.model.calendar; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.onebusaway.gtfs.DateSupport.date; import java.text.ParseException; @@ -26,8 +26,7 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.Test; -import org.onebusaway.gtfs.model.calendar.ServiceDate; +import org.junit.jupiter.api.Test; public class ServiceDateTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceIdIntervalsTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceIdIntervalsTest.java index 9c85644b8..87cffbcdf 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceIdIntervalsTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/calendar/ServiceIdIntervalsTest.java @@ -15,11 +15,11 @@ */ package org.onebusaway.gtfs.model.calendar; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.TimeZone; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; public class ServiceIdIntervalsTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java index dfaf49d7b..0745742de 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java @@ -15,20 +15,15 @@ */ package org.onebusaway.gtfs.serialization; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Agency; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java index d5bb63ad7..6acba3215 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs.serialization; -import static junit.framework.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.onebusaway.gtfs.serialization.GtfsReaderTest.processFeed; import java.io.IOException; import java.time.LocalTime; import java.util.ArrayList; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.StopTime; import org.onebusaway.gtfs.services.GtfsRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java index 880377d60..0a523aa43 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java @@ -15,16 +15,16 @@ */ package org.onebusaway.gtfs.serialization; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Location; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderDefaultAgencyIdTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderDefaultAgencyIdTest.java index c9176bb32..4aca79d38 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderDefaultAgencyIdTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderDefaultAgencyIdTest.java @@ -18,7 +18,7 @@ import java.io.IOException; import java.io.StringReader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.Route; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderStopsTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderStopsTest.java index 348adfd57..88b95ed99 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderStopsTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderStopsTest.java @@ -16,15 +16,15 @@ */ package org.onebusaway.gtfs.serialization; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.csv_entities.exceptions.MissingRequiredFieldException; import org.onebusaway.gtfs.model.Agency; @@ -35,7 +35,7 @@ public class GtfsReaderStopsTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void setup() throws IOException { _gtfs = MockGtfs.create(); _gtfs.putDefaultAgencies(); @@ -50,7 +50,7 @@ public void setup() throws IOException { */ @Test - @Ignore + @Disabled public void testMissingStopLat() throws IOException { _gtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon", "1,The Stop, ,-122.0"); @@ -64,7 +64,7 @@ public void testMissingStopLat() throws IOException { } @Test - @Ignore + @Disabled public void testMissingStopLon() throws IOException { _gtfs.putLines("stops.txt", "stop_id,stop_name,stop_lat,stop_lon", "1,The Stop,47.0,"); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java index dc6b7724f..7579f3074 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java @@ -16,32 +16,29 @@ */ package org.onebusaway.gtfs.serialization; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.io.StringReader; -import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import java.util.Iterator; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; import org.onebusaway.csv_entities.exceptions.InvalidValueEntityException; import org.onebusaway.csv_entities.exceptions.MissingRequiredFieldException; import org.onebusaway.gtfs.GtfsTestData; -import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.*; import org.onebusaway.gtfs.model.calendar.ServiceDate; import org.onebusaway.gtfs.serialization.mappings.AgencyNotFoundForRouteException; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsWriterTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsWriterTest.java index b8c47d6b5..24fe90c38 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsWriterTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsWriterTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs.serialization; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.impl.FileSupport; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; @@ -32,7 +32,7 @@ public class GtfsWriterTest { private FileSupport _support = new FileSupport(); private File _tmpDirectory; - @Before + @BeforeEach public void setup() throws IOException { _tmpDirectory = File.createTempFile("GtfsWriterTest-", "-tmp"); if (_tmpDirectory.exists()) @@ -41,7 +41,7 @@ public void setup() throws IOException { _support.markForDeletion(_tmpDirectory); } - @After + @AfterEach public void teardown() { _support.cleanup(); } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/LocationsGeoJSONReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/LocationsGeoJSONReaderTest.java index cab0e3f93..f2fbc858d 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/LocationsGeoJSONReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/LocationsGeoJSONReaderTest.java @@ -17,7 +17,7 @@ package org.onebusaway.gtfs.serialization; import org.geojson.LngLatAlt; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Location; import org.geojson.Polygon; @@ -27,8 +27,8 @@ import java.io.InputStreamReader; import java.util.Collection; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class LocationsGeoJSONReaderTest { diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java index 97534e277..b5ce4fe90 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/VehiclesExtReaderTest.java @@ -15,7 +15,7 @@ */ package org.onebusaway.gtfs.serialization; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.GtfsTestData; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; @@ -25,8 +25,8 @@ import java.io.IOException; import java.util.Collection; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class VehiclesExtReaderTest extends BaseGtfsTest{ diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/LatLonFieldMappingFactoryTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/LatLonFieldMappingFactoryTest.java index 4ead77180..a15c72815 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/LatLonFieldMappingFactoryTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/LatLonFieldMappingFactoryTest.java @@ -15,14 +15,14 @@ */ package org.onebusaway.gtfs.serialization.mappings; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.CsvEntityContextImpl; import org.onebusaway.csv_entities.schema.BeanWrapperFactory; import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; @@ -34,7 +34,7 @@ public class LatLonFieldMappingFactoryTest { private FieldMapping _fieldMapping; - @Before + @BeforeEach public void before() { _fieldMapping = buildFieldMapping(); } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/ServiceDateFieldMappingFactoryTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/ServiceDateFieldMappingFactoryTest.java index 479408c30..b66a06a65 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/ServiceDateFieldMappingFactoryTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/ServiceDateFieldMappingFactoryTest.java @@ -15,12 +15,12 @@ */ package org.onebusaway.gtfs.serialization.mappings; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.CsvEntityContext; import org.onebusaway.csv_entities.CsvEntityContextImpl; import org.onebusaway.csv_entities.schema.BeanWrapper; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/StopTimeFieldMappingFactoryTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/StopTimeFieldMappingFactoryTest.java index a839d1264..e68d4d881 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/StopTimeFieldMappingFactoryTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/mappings/StopTimeFieldMappingFactoryTest.java @@ -16,15 +16,15 @@ */ package org.onebusaway.gtfs.serialization.mappings; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.onebusaway.gtfs.serialization.mappings.StopTimeFieldMappingFactory.getStringAsSeconds; import static org.onebusaway.gtfs.serialization.mappings.StopTimeFieldMappingFactory.getSecondsAsString; import java.util.HashMap; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.onebusaway.csv_entities.CsvEntityContext; import org.onebusaway.csv_entities.CsvEntityContextImpl; import org.onebusaway.csv_entities.schema.BeanWrapper; diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/services/MockGtfsTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/services/MockGtfsTest.java index 2099ac324..4477b883e 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/services/MockGtfsTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/services/MockGtfsTest.java @@ -15,13 +15,13 @@ */ package org.onebusaway.gtfs.services; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.model.calendar.ServiceDate; @@ -30,7 +30,7 @@ public class MockGtfsTest { private MockGtfs _gtfs; - @Before + @BeforeEach public void before() throws IOException { _gtfs = MockGtfs.create(); } diff --git a/pom.xml b/pom.xml index fa1e18c9c..86dd9f153 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,12 @@ org.onebusaway onebusaway-collections ${onebusaway_collections_version} + + + junit + junit + + org.slf4j @@ -117,12 +123,6 @@ 5.6.2 test - - org.junit.vintage - junit-vintage-engine - 5.6.2 - test - org.mockito mockito-core @@ -201,6 +201,18 @@ maven-jar-plugin 3.0.1 + + org.apache.maven.plugins + maven-surefire-plugin + 3.3.1 + + + me.fabriciorby + maven-surefire-junit5-tree-reporter + 1.3.0 + + + com.mycila license-maven-plugin From 423ba4ad862438dda11c6a9d3657d0abc7d5ec0b Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 29 Aug 2024 10:18:01 +0300 Subject: [PATCH 033/234] Add Experimental annotation. --- .../gtfs/annotations/Experimental.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/annotations/Experimental.java diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/annotations/Experimental.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/annotations/Experimental.java new file mode 100644 index 000000000..905612c9e --- /dev/null +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/annotations/Experimental.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2024 OneBusAway contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Documented; + +/** + * This annotation indicates that a field is experimental. + */ +@Retention(value = RetentionPolicy.SOURCE) +@Target(value = ElementType.FIELD) +@Documented +public @interface Experimental { + /** + * This indicates what issue this field was proposed in. + * Ideally this should be a URL. + */ + String proposedBy(); +} From 204ec3678402108eb983350073fd9c20d21ff705 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 26 Aug 2024 13:12:48 +0300 Subject: [PATCH 034/234] Add 'cars_allowed' to Trip and add test. --- .../java/org/onebusaway/gtfs/model/Trip.java | 24 +++++++++++++++++++ .../gtfs/serialization/GtfsReaderTest.java | 5 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index e6611f8e3..843103055 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -17,6 +17,7 @@ import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.gtfs.annotations.Experimental; import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; import org.onebusaway.gtfs.serialization.mappings.EntityFieldMappingFactory; import org.onebusaway.gtfs.serialization.mappings.TripAgencyIdFieldMappingFactory; @@ -100,6 +101,13 @@ public final class Trip extends IdentityBean { @CsvField(optional = true, defaultValue = "0") private int bikesAllowed = 0; + /** + * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed + */ + @Experimental(proposedBy="https://github.com/google/transit/issues/466") + @CsvField(optional = true, defaultValue = "0") + private int carsAllowed = 0; + // Custom extension for KCM to specify a fare per-trip @CsvField(optional = true) private String fareId; @@ -153,6 +161,7 @@ public Trip(Trip obj) { this.safeDurationOffset = obj.safeDurationOffset; this.tripBikesAllowed = obj.tripBikesAllowed; this.bikesAllowed = obj.bikesAllowed; + this.carsAllowed = obj.carsAllowed; this.fareId = obj.fareId; this.note = obj.note; this.peakOffpeak = obj.peakOffpeak; @@ -353,6 +362,21 @@ public void setBikesAllowed(int bikesAllowed) { this.bikesAllowed = bikesAllowed; } + /** + * @return 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed + */ + public int getCarsAllowed() { + return carsAllowed; + } + + /** + * @param carsAllowed 0 = unknown / unspecified, 1 = cars allowed, 2 = cars + * NOT allowed + */ + public void setCarsAllowed(int carsAllowed) { + this.carsAllowed = carsAllowed; + } + public String toString() { return ""; } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java index 7579f3074..0ba05c737 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java @@ -75,8 +75,8 @@ public void testAllFields() throws IOException { gtfs.putLines( "trips.txt", "route_id,service_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,route_short_name," - + "trip_bikes_allowed,bikes_allowed,wheelchair_accessible,peak_offpeak", - "R1,WEEK,T1,head-sign,short-name,1,B1,SHP1,10X,1,2,1,3"); + + "trip_bikes_allowed,bikes_allowed,wheelchair_accessible,peak_offpeak,cars_allowed", + "R1,WEEK,T1,head-sign,short-name,1,B1,SHP1,10X,1,2,1,3,1"); gtfs.putLines( "stop_times.txt", "trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type," @@ -210,6 +210,7 @@ public void testAllFields() throws IOException { assertEquals("10X", trip.getRouteShortName()); assertEquals(1, trip.getTripBikesAllowed()); assertEquals(2, trip.getBikesAllowed()); + assertEquals(1, trip.getCarsAllowed()); assertEquals(1, trip.getWheelchairAccessible()); assertEquals(3, trip.getPeakOffpeak()); From 21fc39e2bbeb472589fe8efc400cdce86765eb53 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:06:10 +0200 Subject: [PATCH 035/234] Prepare deployment to Maven Central --- pom.xml | 107 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 41 deletions(-) diff --git a/pom.xml b/pom.xml index 86dd9f153..00a4df8c7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,8 @@ 4.0.0 - - org.onebusaway - onebusaway - 1.2.9 - + org.onebusaway onebusaway-gtfs-modules 1.4.18-SNAPSHOT pom @@ -15,6 +11,22 @@ A collection of GTFS libraries and tools. https://github.com/OneBusAway/onebusaway-gtfs-modules/wiki/ + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.html + + + + + + Sheldon Brown + + + Leonard Ehrenfried + + + 1.1.7 1.2.8 @@ -42,22 +54,10 @@ releases-camsys-public-repo https://repo.camsys-apps.com/releases/ - - true - - - false - snapshots-camsys-public-repo https://repo.camsys-apps.com/snapshots/ - - false - - - true - @@ -73,7 +73,7 @@ onebusaway-gtfs-hibernate-cli onebusaway-gtfs-transformer onebusaway-gtfs-transformer-cli - onebusaway-gtfs-transformer-cli-aws + onebusaway-gtfs-merge onebusaway-gtfs-merge-cli @@ -160,46 +160,71 @@ - org.apache.maven.plugins - maven-javadoc-plugin - 3.4.1 + maven-compiler-plugin + 3.10.1 - - -Xdoclint:none - 8 - false + 11 + 11 + + + + org.apache.maven.plugins + maven-site-plugin + 3.12.1 + + + org.sonatype.central + central-publishing-maven-plugin + 0.5.0 + true + + central + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 - - attach-javadocs + attach-sources - jar + jar-no-fork org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 + maven-javadoc-plugin + 3.7.0 - 11 - 11 + none + false + + + attach-javadocs + + jar + + + org.apache.maven.plugins - maven-site-plugin - 3.12.1 - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.1 + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + org.apache.maven.plugins From d7fa7c253e93006e05eca20ec993d6a2c21d83d9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:09:43 +0200 Subject: [PATCH 036/234] Update CI tasks --- .github/workflows/ci.yml | 19 ++++++------------- onebusaway-gtfs-transformer/pom.xml | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f516e09e6..a46e7dc24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,23 +11,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v1 + - uses: actions/setup-java@v4 with: - java-version: 11 - - - name: Cache Maven dependencies - uses: actions/cache@v3 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + distribution: 'temurin' + java-version: '11' + cache: 'maven' - name: Test project with Maven - run: mvn --no-transfer-progress test install + run: mvn --no-transfer-progress test package - name: Build documentation run: mvn --no-transfer-progress site diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 44356ec57..618b15e0d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -16,7 +16,7 @@ org.onebusaway onebusaway-gtfs - 1.4.18-SNAPSHOT + 1.4.t 18-SNAPSHOT org.onebusaway From 4acdebf63781564cc5a403cd36876bcb0f90edd1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:18:20 +0200 Subject: [PATCH 037/234] Update version --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 618b15e0d..2aaad29e4 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -16,7 +16,7 @@ org.onebusaway onebusaway-gtfs - 1.4.t 18-SNAPSHOT + ${project.version} org.onebusaway From 4fa463a15889d6b6b42ccf0ecaefe4403391dcda Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:34:17 +0200 Subject: [PATCH 038/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-1.4.18 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 5 ++--- pom.xml | 7 +++---- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 85e8c3e9b..b90c18783 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 55bcf42e7..e7c38211a 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 76e668965..1c844f53b 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.18-SNAPSHOT + 1.4.18 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 6bfe28927..28592be5d 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.18-SNAPSHOT + 1.4.18 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 7fa6d376f..e4c991b8b 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 2aaad29e4..a7cda7d6a 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index c3c06fe85..e4689f4e8 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 onebusaway-gtfs jar @@ -10,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 diff --git a/pom.xml b/pom.xml index 00a4df8c7..a6334fe87 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 1.4.18-SNAPSHOT + 1.4.18 pom onebusaway-gtfs-modules @@ -38,7 +37,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-1.4.18 From 8c485e064d70d1c8b08631dbacf5f72bc9de6c83 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:34:19 +0200 Subject: [PATCH 039/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index b90c18783..7b222124e 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index e7c38211a..378934a3b 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 1c844f53b..569943ef1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.18 + 1.4.19-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 28592be5d..5474c89a8 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.18 + 1.4.19-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index e4c991b8b..efd8500d0 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a7cda7d6a..45df0df56 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index e4689f4e8..9d50cd87d 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT diff --git a/pom.xml b/pom.xml index a6334fe87..563f3931c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.18 + 1.4.19-SNAPSHOT pom onebusaway-gtfs-modules @@ -37,7 +37,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-1.4.18 + HEAD From 40f5f99dfa07c2cde02ff1a756f364874129f54f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:43:14 +0200 Subject: [PATCH 040/234] Autopublish --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 563f3931c..35faaa28e 100644 --- a/pom.xml +++ b/pom.xml @@ -179,6 +179,8 @@ true central + true + published From b59522b564ee27e2522c1e923773537512afdd21 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:49:48 +0200 Subject: [PATCH 041/234] Use central repo --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 35faaa28e..d1414aa23 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,11 @@ + + central2 + Check central first to avoid a lot of not found warnings + https://repo.maven.apache.org/maven2 + repo.camsys-apps.com https://repo.camsys-apps.com/third-party/ From 81a43df340a786dc6765a4eefce8bef513ae496f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 22:52:02 +0200 Subject: [PATCH 042/234] Remove license header plugin --- onebusaway-gtfs-transformer/pom.xml | 71 +++++++++++++---------------- onebusaway-gtfs/pom.xml | 14 ------ pom.xml | 11 ----- 3 files changed, 31 insertions(+), 65 deletions(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 45df0df56..b9c7c7d92 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 onebusaway-gtfs-transformer @@ -38,11 +39,11 @@ junit-jupiter-api test - - org.slf4j - slf4j-simple - ${slf4j_version} - + + org.slf4j + slf4j-simple + ${slf4j_version} + org.mockito mockito-core @@ -53,51 +54,41 @@ wsf-api 1.0 - - org.onebusaway - onebusaway-cloud-api - ${onebusaway_cloud_version} - + + org.onebusaway + onebusaway-cloud-api + ${onebusaway_cloud_version} + org.onebusaway onebusaway-cloud-noop ${onebusaway_cloud_version} - - javax.xml.bind - jaxb-api - - - com.sun.xml.bind - jaxb-core - - - com.sun.xml.bind - jaxb-impl - + + javax.xml.bind + jaxb-api + + + com.sun.xml.bind + jaxb-core + + + com.sun.xml.bind + jaxb-impl + onebusaway-gtfs-transformer - com.mycila - license-maven-plugin - - - src/test/resources/org/onebusaway/gtfs_transformer/** - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 9d50cd87d..df333e054 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -55,18 +55,4 @@ - - - - - com.mycila - license-maven-plugin - - - src/test/resources/org/onebusaway/gtfs/** - - - - - diff --git a/pom.xml b/pom.xml index d1414aa23..d15c49c75 100644 --- a/pom.xml +++ b/pom.xml @@ -244,17 +244,6 @@ - - com.mycila - license-maven-plugin - -
LICENSE.txt
- - **/ci.yml - **/simplelogger.properties - -
-
From b2f899aaa2ee9447ae2132619bb1b013eb1ffc5c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:02:15 +0200 Subject: [PATCH 043/234] Configure reports --- pom.xml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d15c49c75..87d36af75 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.onebusaway @@ -157,6 +158,14 @@ org.apache.maven.plugins maven-project-info-reports-plugin 3.4.2 + + + + index + licenses + + + @@ -246,4 +255,5 @@ + From 3971599088ef9a0c8a1c5200f14d3dfe95ae60a1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:05:49 +0200 Subject: [PATCH 044/234] Update parent pom --- onebusaway-gtfs/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index df333e054..97d40bffc 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -10,6 +10,7 @@ org.onebusaway onebusaway-gtfs-modules 1.4.19-SNAPSHOT + ../pom.xml From 300cc55f22592c477d540bed5e08884cb14b2ba7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:08:57 +0200 Subject: [PATCH 045/234] Add relative path --- onebusaway-gtfs-hibernate/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 378934a3b..db1b7d8c3 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -10,6 +10,7 @@ org.onebusaway onebusaway-gtfs-modules 1.4.19-SNAPSHOT + ../pom.xml From 6962a9364bce6c441cd536aa09aa1da1e644600f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:10:31 +0200 Subject: [PATCH 046/234] Add encoding --- onebusaway-gtfs-hibernate-cli/pom.xml | 1 + pom.xml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 7b222124e..25b809153 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -4,6 +4,7 @@ org.onebusaway onebusaway-gtfs-modules 1.4.19-SNAPSHOT + ../pom.xml onebusaway-gtfs-hibernate-cli onebusaway-gtfs-hibernate-cli diff --git a/pom.xml b/pom.xml index 87d36af75..fb1fe7304 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,8 @@ 1.2.8 2.0.6 0.0.13 + UTF-8 + UTF-8
From 4ff9b1c1024cdd3f3d4fea64e8f4456d2ffbf682 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:16:02 +0200 Subject: [PATCH 047/234] Skip site plugin --- .github/workflows/ci.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a46e7dc24..40fe1498c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,12 +22,12 @@ jobs: - name: Test project with Maven run: mvn --no-transfer-progress test package - - name: Build documentation - run: mvn --no-transfer-progress site - - - name: Deploy documentation to Github Pages - # only deploy after merging to master - if: github.repository_owner == 'OneBusAway' && github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: JamesIves/github-pages-deploy-action@v4 - with: - folder: target/site/ +# - name: Build documentation +# run: mvn --no-transfer-progress site +# +# - name: Deploy documentation to Github Pages +# # only deploy after merging to master +# if: github.repository_owner == 'OneBusAway' && github.event_name == 'push' && github.ref == 'refs/heads/master' +# uses: JamesIves/github-pages-deploy-action@v4 +# with: +# folder: target/site/ From 7ba9aed08ed1513fbdebdc0e8ef133aa4124b150 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:29:22 +0200 Subject: [PATCH 048/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-2.0.0 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 5 ++--- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 7 +++---- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 25b809153..7e7447f0d 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index db1b7d8c3..0e2c60c4b 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 569943ef1..ada162229 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.19-SNAPSHOT + 2.0.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 5474c89a8..6bd10293e 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 1.4.19-SNAPSHOT + 2.0.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index efd8500d0..acda3c54a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index b9c7c7d92..165f70d3a 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 onebusaway-gtfs-transformer @@ -10,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 97d40bffc..fe8f0f90a 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 ../pom.xml diff --git a/pom.xml b/pom.xml index fb1fe7304..ebecebb97 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 1.4.19-SNAPSHOT + 2.0.0 pom onebusaway-gtfs-modules @@ -40,7 +39,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-2.0.0 From 5daa2a5feebbf2218e5ff6b819d2ad32bda9023b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:29:25 +0200 Subject: [PATCH 049/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 7e7447f0d..564ec254a 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 0e2c60c4b..53f97b313 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index ada162229..9343495d1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.0 + 2.0.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 6bd10293e..0cd80835b 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.0 + 2.0.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index acda3c54a..7d98c5197 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 165f70d3a..2f852a6ef 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index fe8f0f90a..f80135bfe 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index ebecebb97..2d248b2b4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.0 + 2.0.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -39,7 +39,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-2.0.0 + HEAD From 3891e15f47b53cfeea219361ffeaa1cd08199e91 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:47:29 +0200 Subject: [PATCH 050/234] Move documentation to Markdown --- Makefile | 2 + src/site/apt/index.apt.vm => docs/index.md | 76 +++--- .../onebusaway-gtfs-hibernate-cli.md | 14 +- .../onebusaway-gtfs-merge-cli.md | 70 +++--- .../onebusaway-gtfs-transformer-cli.md | 174 +++++++------- pom.xml | 1 - src/site/apt/release-notes.apt.vm | 223 ------------------ 7 files changed, 166 insertions(+), 394 deletions(-) create mode 100644 Makefile rename src/site/apt/index.apt.vm => docs/index.md (81%) rename src/site/apt/onebusaway-gtfs-hibernate-cli.apt.vm => docs/onebusaway-gtfs-hibernate-cli.md (76%) rename src/site/apt/onebusaway-gtfs-merge-cli.apt.vm => docs/onebusaway-gtfs-merge-cli.md (63%) rename src/site/apt/onebusaway-gtfs-transformer-cli.apt.vm => docs/onebusaway-gtfs-transformer-cli.md (91%) delete mode 100644 src/site/apt/release-notes.apt.vm diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..af44d2924 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +release: + mvn release:prepare release:perform -Dgoals=deploy release:clean diff --git a/src/site/apt/index.apt.vm b/docs/index.md similarity index 81% rename from src/site/apt/index.apt.vm rename to docs/index.md index f8445a862..dae952433 100644 --- a/src/site/apt/index.apt.vm +++ b/docs/index.md @@ -1,26 +1,22 @@ -onebusaway-gtfs-modules +# onebusaway-gtfs-modules We provide a Java library for reading and writing {{{https://developers.google.com/transit/gtfs} GTFS}} feeds, including database support. - <> ${currentVersion} - - Details on all releases can be found in the {{{./release-notes.html}Release Notes}}. - The library is broken up into a few key modules: - * <<>> - The core library for reading and writing GTFS + * `onebusaway-gtfs` - The core library for reading and writing GTFS - * <<>> - Support for {{{http://www.hibernate.org/}Hibernate}} database persistence of GTFS data + * `onebusaway-gtfs-hibernate` - Support for {{{http://www.hibernate.org/}Hibernate}} database persistence of GTFS data - * <<>> - Command-line utilty for loading GTFS feeds into a database - see {{{./onebusaway-gtfs-hibernate-cli.html}the full documentation}}. + * `onebusaway-gtfs-hibernate-cli` - Command-line utilty for loading GTFS feeds into a database - see {{{./onebusaway-gtfs-hibernate-cli.html}the full documentation}}. - * <<>> - Tools for transforming GTFS data + * `onebusaway-gtfs-transformer` - Tools for transforming GTFS data - * <<>> - Command-line utility for transforming GTFS - see {{{./onebusaway-gtfs-transformer-cli.html}the full documentation}}. + * `onebusaway-gtfs-transformer-cli` - Command-line utility for transforming GTFS - see {{{./onebusaway-gtfs-transformer-cli.html}the full documentation}}. - * <<>> - Tools for merging GTFS data + * `onebusaway-gtfs-merge` - Tools for merging GTFS data - * <<>> - Command-line utility for merging GTFS feeds - see {{{./onebusaway-gtfs-merge-cli.html}the full documentation}}. + * `onebusaway-gtfs-merge-cli` - Command-line utility for merging GTFS feeds - see {{{./onebusaway-gtfs-merge-cli.html}the full documentation}}. Documentation @@ -30,7 +26,7 @@ Using in Maven The library is available as a Maven module. Simply add the following dependencies: -+---+ +``` @@ -51,7 +47,7 @@ Using in Maven ${currentVersion} -+---+ +``` #if( $currentVersion.contains('SNAPSHOT') ) To use a SNAPSHOT version of the library, you'll need to {{{https://github.com/OneBusAway/onebusaway/wiki/Maven-Repository}add a reference to the OneBusAway Maven repository}}. @@ -63,7 +59,7 @@ Example Code Let's introduce basic code for reading a GTFS feed and handling the resulting entities: -+---+ +``` public class GtfsReaderExampleMain { public static void main(String[] args) throws IOException { @@ -107,7 +103,7 @@ public class GtfsReaderExampleMain { } } } -+---+ +``` Notice that the {{{./apidocs/org/onebusaway/gtfs/serialization/GtfsReader.html}GtfsReader}} does the bulk of the work of reading the GTFS feed. The general pattern is to create the reader, set the input file, and call `run()` to start the reading process. You can manage the resulting GTFS entities in a couple of ways: @@ -118,7 +114,7 @@ public class GtfsReaderExampleMain { * Basic Writing -+---+ +``` public class GtfsWriterExampleMain { public static void main(String[] args) throws IOException { @@ -146,17 +142,17 @@ public class GtfsWriterExampleMain { writer.close(); } } -+---+ +``` * Basic Database Reading - The class <<>> in the -<<>> directory includes basic code for reading + The class `org.onebusaway.gtfs.examples.GtfsHibernateReaderExampleMain` in the +`onebusaway-gtfs-hibernate/src/test/java` directory includes basic code for reading a GTFS feed into a database and querying the resulting entities. The sample code has been summarized for length and clarity: -+---+ +``` public class GtfsHibernateReaderExampleMain { public static void main(String[] args) throws IOException { @@ -200,10 +196,10 @@ public class GtfsHibernateReaderExampleMain { return new HibernateGtfsFactory(sessionFactory); } } -+---+ +``` This code is roughly similar to the example reader code for -<<>>, with the main difference being the use of <<>>, which is a convenience +`onebusaway-gtfs`, with the main difference being the use of `HibernateGtfsFactory`, which is a convenience factory for creating database-aware DAOs. @@ -214,13 +210,13 @@ it to use a different database and you totally can. See {{http://hibernate.org/ Hibernate, but also check out the default hibernate config file used in the example above. It's located in the following directory: -+---+ +``` onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/examples/hibernate-configuration-examples.xml -+---+ +``` The contents look like: -+---+ +``` @@ -236,25 +232,25 @@ onebusaway-gtfs-hibernate/src/test/resources/org/onebusaway/gtfs/examples/hibern -+---+ +``` Here you can configure the data source used for the database connection along with the Hibernate dialect. * Reading Custom Fields - Does your GTFS feed have custom fields not defined by the core <<>> library? It's possible + Does your GTFS feed have custom fields not defined by the core `onebusaway-gtfs` library? It's possible to read and write this data without modify OBA source code using the "extensions" mechanism. Consider a -<<>> file with a custom <<>> field: +`stops.txt` file with a custom `extra_stop_info` field: -+---+ +``` stop_id,stop_name,stop_lat,stop_lon,extra_stop_info 123,Some Station,47.0,-122.0,This is a cool transit station -+---+ +``` - The <<>> field isn't included in the the {{{./apidocs/org/onebusaway/gtfs/model/Stop.html}Stop}} data -model by default. So instead, we define a special <<>> Java bean type with the new field: + The `extra_stop_info` field isn't included in the the {{{./apidocs/org/onebusaway/gtfs/model/Stop.html}Stop}} data +model by default. So instead, we define a special `StopExtension` Java bean type with the new field: -+---+ +``` public static class StopExtension { @CsvField(optional = true) private String extraStopInfo; @@ -262,26 +258,26 @@ public static class StopExtension { public String getExtraStopInfo() { ... } public void setExtraStopInfo(String info) { ... } } -+---+ +``` We can now register our class as an extension of the default stop data model: -+---+ +``` DefaultEntitySchemaFactory factory = GtfsEntitySchemaFactory.createEntitySchemaFactory(); factory.addExtension(Stop.class, StopExtension.class); GtfsReader reader = new GtfsReader(); reader.setEntitySchemaFactory(factory); -+---+ +``` - Now when you read your GTFS feed with the <<>> instance, <<>> objects + Now when you read your GTFS feed with the `GtfsReader` instance, `StopExtension` objects will automatically be created, populated, and attached to stops as they are read: -+---+ +``` Stop stop = dao.getStopForId(...); StopExtension extension = stop.getExtension(StopExtension.class); System.out.println(extension.getExtraStopInfo()); -+---+ +``` For more information on defining the mapping from GTFS fields to Java beans, see documentation for the {{{https://github.com/OneBusAway/onebusaway-csv-entities}onebusaway-csv-entities}} project, diff --git a/src/site/apt/onebusaway-gtfs-hibernate-cli.apt.vm b/docs/onebusaway-gtfs-hibernate-cli.md similarity index 76% rename from src/site/apt/onebusaway-gtfs-hibernate-cli.apt.vm rename to docs/onebusaway-gtfs-hibernate-cli.md index 9e7af5bcc..b80a45ce3 100644 --- a/src/site/apt/onebusaway-gtfs-hibernate-cli.apt.vm +++ b/docs/onebusaway-gtfs-hibernate-cli.md @@ -6,7 +6,7 @@ GTFS Hibernate Command-Line Utility Introduction - The <<>> command-line utility is a simple command-line tool for loading a + The `onebusaway-gtfs-hibernate-cli` command-line utility is a simple command-line tool for loading a {{{https://developers.google.com/transit/gtfs}GTFS}} feed into a database. Getting the Application @@ -27,7 +27,7 @@ Using the Application You'll need a Java 11 runtime installed to run the client. To run the application: -+---+ +``` java -classpath onebusaway-gtfs-hiberante-cli.jar:your-database-jdbc.jar \ org.onebusaway.gtfs.GtfsDatabaseLoaderMain \ --driverClass=... \ @@ -35,7 +35,7 @@ java -classpath onebusaway-gtfs-hiberante-cli.jar:your-database-jdbc.jar \ --username=... \ --password=... \ gtfs_path -+---+ +``` Note that the utility doesn't include any JDBC client jars for any databases by default. You will need to download an appropriate JDBC client for your database and include it on the classpath when running @@ -44,13 +44,13 @@ using the command-line arguments specified below. * Arguments - * <<<--driverClass=...>>> : JDBC driver class for your JDBC provider (eg. "org.hsqldb.jdbcDriver") + * `--driverClass=...` : JDBC driver class for your JDBC provider (eg. "org.hsqldb.jdbcDriver") - * <<<--url=...>>> : JDBC connection url for your database (eg. "jdbc:hsqldb:mem:temp_db") + * `--url=...` : JDBC connection url for your database (eg. "jdbc:hsqldb:mem:temp_db") - * <<<--username=...>>> : JDBC connection username + * `--username=...` : JDBC connection username - * <<<--password=...>>> : JDBC connection password + * `--password=...` : JDBC connection password [] diff --git a/src/site/apt/onebusaway-gtfs-merge-cli.apt.vm b/docs/onebusaway-gtfs-merge-cli.md similarity index 63% rename from src/site/apt/onebusaway-gtfs-merge-cli.apt.vm rename to docs/onebusaway-gtfs-merge-cli.md index d6ebbef5a..a8e545728 100644 --- a/src/site/apt/onebusaway-gtfs-merge-cli.apt.vm +++ b/docs/onebusaway-gtfs-merge-cli.md @@ -8,7 +8,7 @@ Introduction <>: This tool is a work in progress! This documentation may not be up-to-date. - The <<>> command-line application is a simple command-line tool for merging + The `onebusaway-gtfs-merge-cli` command-line application is a simple command-line tool for merging {{{https://developers.google.com/transit/gtfs}GTFS}} feeds. Getting the Application @@ -29,50 +29,50 @@ Using the Application You'll need a Java 11 runtime installed to run the client. To run the application: -+---+ +``` java -jar onebusaway-gtfs-merge-cli.jar [--args] input_gtfs_path_a input_gtfs_path_b ... output_gtfs_path -+---+ +``` <>: Merging large GTFS feeds is often processor and memory intensive. You'll likely need to increase the -max amount of memory allocated to Java with an option like <<<-Xmx1G>>> (adjust the limit as needed). I also recommend -adding the <<<-server>>> argument if you are running the Oracle or OpenJDK, as it can really increase performance. +max amount of memory allocated to Java with an option like `-Xmx1G` (adjust the limit as needed). I also recommend +adding the `-server` argument if you are running the Oracle or OpenJDK, as it can really increase performance. Configuring the Application The merge application supports a number of options and arguments for configuring the application's behavior. The -general pattern is to specify options for each type of file in a GTFS feed using the <<<--file>>> option, specifying -specific options for each file type after the <<<--file>>> option. Here's a quick example: +general pattern is to specify options for each type of file in a GTFS feed using the `--file` option, specifying +specific options for each file type after the `--file` option. Here's a quick example: -+---+ +``` --file=routes.txt --duplicateDetection=identity --file=calendar.txt --logDroppedDuplicates ... -+---+ +``` The merge application supports merging the following files: - * <<>> + * `agency.txt` - * <<>> + * `stops.txt` - * <<>> + * `routes.txt` - * <<>> and <<>> + * `trips.txt` and `stop_times.txt` - * <<>> and <<>> + * `calendar.txt` and `calendar_dates.txt` - * <<>> + * `shapes.txt` - * <<>> + * `fare_attributes.txt` - * <<>> + * `fare_rules.txt` - * <<>> + * `frequencies.txt` - * <<>> + * `transfers.txt` [] - You can specify merge options for each of these files using the <<<--file=gtfs_file.txt>>> option. File types listed -together (eg. <<> and <<>>) are handled by the same merge strategy, so specifying options for + You can specify merge options for each of these files using the `--file=gtfs_file.txt` option. File types listed +together (eg. `trips.txt>> and `stop_times.txt`) are handled by the same merge strategy, so specifying options for either will have the same effect. For details on options you might specify, read on. Handling Duplicates @@ -84,17 +84,17 @@ including how to identify duplicates and what to do with duplicates when they ar We support a couple of methods for determining when entries from two different feeds are actually duplicates. By default, the merge tool will attempt to automatically determine the best merge strategy to use. You can also control the specific -strategy used on a per-file basis using the <<<--duplicateDetection>>> argument. You can specify any of the following +strategy used on a per-file basis using the `--duplicateDetection` argument. You can specify any of the following strategies for duplicate detection. - * <<<--duplicateDetection=identity>>> - If two entries have the same id (eg. stop id, route id, trip id), then they are + * `--duplicateDetection=identity` - If two entries have the same id (eg. stop id, route id, trip id), then they are considered the same. This is the more strict matching policy. - * <<<--duplicateDetection=fuzzy>>> - If two entries have common elements (eg. stop name or location, route short name, + * `--duplicateDetection=fuzzy` - If two entries have common elements (eg. stop name or location, route short name, trip stop sequence), then they are considered the same. This is the more lenient matching policy, and is highly dependent on the type of GTFS entry being matched. - * <<<--duplicateDetection=none>>> - Entries between two feeds are never considered to be duplicates, even if they have + * `--duplicateDetection=none` - Entries between two feeds are never considered to be duplicates, even if they have the same id or similar properties. * Logging Duplicates @@ -102,9 +102,9 @@ strategies for duplicate detection. Sometimes your feed might have unexpected duplicates. You can tell the merge tool to log duplicates it finds or even immediately exit with the following arguments: - * <<<--logDroppedDuplicates>>> - log a message when a duplicate is found + * `--logDroppedDuplicates` - log a message when a duplicate is found - * <<<--errorOnDroppedDuplicates>>> - throw an exception when a duplicate is found, stopping the program + * `--errorOnDroppedDuplicates` - throw an exception when a duplicate is found, stopping the program Examples @@ -114,20 +114,20 @@ Examples service change and a different GTFS feed for after. We'd like to be able to merge these disjoint feeds into one feed with continuous coverage. - In our example, an agency produces two feeds where the entries in <<>> and <<>> are exactly -the same, so the default policy of identifying and dropping duplicates will work fine there. The <<>> file + In our example, an agency produces two feeds where the entries in `agency.txt` and `stops.txt` are exactly +the same, so the default policy of identifying and dropping duplicates will work fine there. The `routes.txt` file is a bit trickier, since the route ids are different between the two feeds but the entries are largely the same. We will use fuzzy duplicate detection to match the routes between the two feeds. - The next issue is the <<>> file. The agency uses the same <<>> values in both feeds -(eg. <<>>, <<>>, <<>>) with different start and end dates in the two feeds. If the default policy of + The next issue is the `calendar.txt` file. The agency uses the same `service_id` values in both feeds +(eg. `WEEK`, `SAT`, `SUN`) with different start and end dates in the two feeds. If the default policy of dropping duplicate entries was used, we'd lose the dates in one of the service periods. Instead, we rename duplicates -such that the service ids from the second feed will be renamed to <<>>, <<>>, etc. and all -<<>> entries in the second feed will be updated appropriately. The result is that trips from the first +such that the service ids from the second feed will be renamed to `b-WEEK`, `b-SAT`, etc. and all +`trips.txt` entries in the second feed will be updated appropriately. The result is that trips from the first and second feed will both have the proper calendar entries in the merged feed. Putting it all together, here is what the command-line options for the application would look like: -+---+ +``` --file=routes.txt --fuzzyDuplicates --file=calendar.txt --renameDuplicates -+---+ \ No newline at end of file +``` \ No newline at end of file diff --git a/src/site/apt/onebusaway-gtfs-transformer-cli.apt.vm b/docs/onebusaway-gtfs-transformer-cli.md similarity index 91% rename from src/site/apt/onebusaway-gtfs-transformer-cli.apt.vm rename to docs/onebusaway-gtfs-transformer-cli.md index 10d283c4a..9868e9414 100644 --- a/src/site/apt/onebusaway-gtfs-transformer-cli.apt.vm +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -8,11 +8,9 @@ Brian Ferris Introduction - The <<>> command-line application is a simple command-line tool for transforming + The `onebusaway-gtfs-transformer-cli` command-line application is a simple command-line tool for transforming {{{https://developers.google.com/transit/gtfs}GTFS}} feeds. -%{toc} - Requirements * Java 11 or greater @@ -35,23 +33,23 @@ Using the Application To run the application: -+---+ +``` java -jar onebusaway-gtfs-transformer-cli.jar [-args] input_gtfs_path ... output_gtfs_path -+---+ +``` -<<>> and <<>> can be either a directory containing a GTFS feed or a .zip file. +`input_gtfs_path` and `output_gtfs_path` can be either a directory containing a GTFS feed or a .zip file. <>: Transforming large GTFS feeds is processor and memory intensive. You'll likely need to increase the -max amount of memory allocated to Java with an option like <<<-Xmx1G>>> or greater. Adding the <<<-server>>> argument +max amount of memory allocated to Java with an option like `-Xmx1G` or greater. Adding the `-server` argument if you are running the Oracle or OpenJDK can also increase performance. * Arguments - * <<<--transform=...>>> : specify a transformation to apply to the input GTFS feed (see syntax below) + * `--transform=...` : specify a transformation to apply to the input GTFS feed (see syntax below) - * <<<--agencyId=id>>> : specify a default agency id for the input GTFS feed + * `--agencyId=id` : specify a default agency id for the input GTFS feed - * <<<--overwriteDuplicates>>> : specify that duplicate GTFS entities should overwrite each other when read + * `--overwriteDuplicates` : specify that duplicate GTFS entities should overwrite each other when read [] @@ -59,27 +57,27 @@ if you are running the Oracle or OpenJDK can also increase performance. Transforms are specified as snippets of example. A simple example to remove a stop might look like: -+---+ +``` {"op":"remove","match":{"file":"stops.txt","stop_name":"Stop NameP"}} -+---+ +``` You can pass those snippets to the application in a couple of ways. The simplest is directly on the command line. -+---+ +``` --transform='{...}' -+---+ +``` - You can have multiple <<<--transform>>> arguments to specify multiple transformations. However, if you have a LOT of + You can have multiple `--transform` arguments to specify multiple transformations. However, if you have a LOT of transformations that you wish to apply, it can be easier to put them in a file, with a JSON snippet per line. Then specify the file on the command-line: -+---+ +``` --transform=path/to/local-file -+---+ +``` You can even specify a URL where the transformations will be read: -+---+ +``` --transform=http://server/path +--+ @@ -89,9 +87,9 @@ Matching updating, retaining, and removing GTFS entities. Many of the transforms accept a "`match`" term that controls how the rule matches against entities: -+---+ +``` {"op":..., "match":{"file":"routes.txt", "route_short_name":"574"}} -+---+ +``` Here, the match snippet at minimum requires a `file` property that specifies the type of GTFS entity to match. Any file name defined in the {{{https://developers.google.com/transit/gtfs/reference#FeedFiles}GTFS specification}} @@ -105,24 +103,24 @@ each file name in the GTFS specification. For example, the snippet above will m Property matching also supports regular expressions that allow you to match property values conforming to a regexp pattern. For example, the snippet below will match any entry in `stops.txt` with a `stop_id` starting with `de:08`. -+---+ +``` {"op":..., "match":{"file":"stops.txt", "stop_id":"m/^de:08.*/"}} -+---+ +``` * Compound Property Expressions Property matching also supports compound property expressions that allow you to match across GTFS relational references. Let's look at a simple example: -+---+ +``` {"op":..., "match":{"file":"trips.txt", "route.route_short_name":"10"}} -+---+ +``` Here the special `routes` property references the route entry associated with each trip, allowing you to match the properties of the route. You can even chain references, like `route.agency` to match against the agency associated with the trip. Here is the full list of supported compound property references: -+---+ +``` {"op":..., "match":{"file":"routes.txt", "agency.name":"Metro"}} {"op":..., "match":{"file":"trips.txt", "route.route_short_name":"10"}} {"op":..., "match":{"file":"stop_times.txt", "stop.stop_id":"153"}} @@ -132,26 +130,26 @@ associated with the trip. Here is the full list of supported compound property {"op":..., "match":{"file":"transfers.txt", "toStop.stop_id":"173"}} {"op":..., "match":{"file":"fare_rules.txt", "fare.currencyType":"USD"}} {"op":..., "match":{"file":"fare_rules.txt", "route.route_short_name":"10"}} -+---+ +``` * Multi-Value Matches The compound property expressions shown above are all for 1-to-1 relations, but matching also supports a limited form of multi-value matching for 1-to-N relations. Let's look at a simple example: -+---+ +``` {"op":..., "match":{"file":"routes.txt", "any(trips.trip_headsign)":"Downtown"}} -+---+ +``` Notice the addition of `any(...)` around the property name. Here we are using a special `trips` property that expands to include all trips associated with each route. Now, if *any* trip belonging to the route has the specified `trip_headsign` value, then the route matches. Here is the full list of supported multi-value property matches: -+---+ +``` {"op":..., "match":{"file":"agency.txt", "any(routes.X)":"Y"}} {"op":..., "match":{"file":"routes.txt", "any(trips.X)":"Y"}} {"op":..., "match":{"file":"trips.txt", "any(stop_times.X)":"Y"}} -+---+ +``` * Collection-Like Entities @@ -160,10 +158,10 @@ shape points in `shapes.txt` linked by a common `shape_id` value or `calendar.tx linked by a common `service_id` value. You can use a special `collection `match clause to match against the entire collection. -+---+ +``` {"op":..., "match":{"collection":"shape", "shape_id":"XYZ"}} {"op":..., "match":{"collection":"calendar", "service_id":"XYZ"}} -+---+ +``` You can use the calendar collection matches, for example, to retain a calendar, including all `calendar.txt`, `calendar_dates.txt`, and `trip.txt` entries that reference the specified `service_id` value. This convenient @@ -175,52 +173,52 @@ Types of Transforms Create and add a new entity to the feed. -+---+ +``` {"op":"add","obj":{"file":"agency.txt", "agency_id":"ST", "agency_name":"Sound Transit", "agency_url":"http://www.soundtransit.org", "agency_timezone":"America/Los_Angeles"}} -+---+ +``` * Update an Entity You can update arbitrary fields of a GTFS entity. -+---+ +``` {"op":"update", "match":{"file":"routes.txt", "route_short_name":"574"}, "update":{"agency_id":"ST"}} -+---+ +``` Normally, update values are used as-is. However, we support a number of special update operations: ** Find/Replace -+---+ +``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} -+---+ +``` - By using <<>> syntax in the update value, the update will perform + By using `s/.../.../` syntax in the update value, the upda```te will perform a find-replace operation on the specified property value. Consider the following example: -+---+ +``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} -+---+ +``` - Here, a trip with a headsign of <<>> will be updated to - <<>>. + Here, a trip with a headsign of `North Seattle` will be updated to + `N Seattle`. ** Path Expressions - By using <<>> syntax in the update value, the expression will be + By using `path(...)` syntax in the update value, the expression will be treated as a compound Java bean properties path expression. This path expression will be evaluated against the target entity to produce the update value. Consider the following example: -+---+ +``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"path(route.longName)"}} -+---+ +``` - Here, the <<>> field is updated for each trip in the feed. - The value will be copied from the <<>> field of each trip's + Here, the `trip_short_name` field is updated for each trip in the feed. + The value will be copied from the `route_long_name` field of each trip's associated route. * Retain an Entity @@ -231,9 +229,9 @@ Types of Transforms In the following example, only route B15 will be retained, along with all the stops, trips, stop times, shapes, and agencies linked to directly by that route. -+---+ +``` {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"B15"}} -+---+ +``` By default, we retain across {{{https://developers.google.com/transit/gtfs/reference#trips_block_id_field}block_id}} values specified in trips.txt. That means if a particular trip is retained (perhaps because its parent route is retained), @@ -241,20 +239,20 @@ and the trip specifies a block_id, then all the trips referencing that block_id their own routes, stop times, and shapes. This can potentially lead to unexpected results if you retain one route and suddenly see other routes included because they are linked by block_id. - You can disable this feature by specifying <<>> in the JSON transformer snippet. Here is an + You can disable this feature by specifying `retainBlocks: false` in the JSON transformer snippet. Here is an example: -+---+ +``` {"op":"retain","match":{"file":"routes.txt", "route_short_name":"B15"}, "retainBlocks":false} -+---+ +``` * Remove an Entity You can remove a specific entity from a feed. -+---+ +``` {"op":"remove", "match":{"file":"stops.txt", "stop_name":"Stop Name"}} -+---+ +``` Note that removing an entity has a cascading effect. If you remove a trip, all the stop times that depend on that trip will also be removed. If you remove a route, all the trips and stop times for that route will be removed. @@ -263,30 +261,30 @@ trip will also be removed. If you remove a route, all the trips and stop times You can remove stop times from the beginning or end of a trip using the "trim_trip" operation. Example: -+---+ +``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "from_stop_id":"138S"} -+---+ +``` For any trip belonging to the specified route and passing through the specified stop, all stop times from the specified stop onward will be removed from the trip. You can also remove stop times from the beginning of the trip as well: -+---+ +``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "to_stop_id":"138S"} -+---+ +``` Or both: -+---+ +``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "to_stop_id":"125S", "from_stop_id":"138S"} -+---+ +``` * Generate Stop Times You can generate stop time entries for a trip. Example: -+---+ +``` {"op":"stop_times_factory", "trip_id":"TRIP01", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["S01", "S02", "S03"]} -+---+ +``` A series of entries in `stop_times.txt` will be generated for the specified trip, traveling along the specified sequence of stops. The departure time for the first stop will be set from the `start_time` field, the arrival time for the last stop will @@ -299,9 +297,9 @@ trip. the feed by hand can be a tedious task, especially when the feed uses a complex combination of `calendar.txt` and `calendar_dates.txt` entries. Fortunately, the GTFS tranformer tool supports a `calendar_extension` operation that can help simplify the work. Example: -+---+ +``` {"op":"calendar_extension", "end_date":"20130331"} -+---+ +``` The operation requires just one argument by default: `end_date` to specify the new end-date for the feed. The operation does its best to intelligently extend each service calendar, as identified by a `service_id` in `calendar.txt` or `calendar_dates.txt`. @@ -319,9 +317,9 @@ and a second calendar active from August 1 to September 31. If it's the last we you typically only mean to extend the second service calendar. You can control this inactive calendar cutoff with an optional argument: -+---+ +``` {"op":"calendar_extension", "end_date":"20130331", "inactive_calendar_cutoff":"20121031"} -+---+ +``` Calendars that have expired before the specified date will be considered inactive and won't be extended. @@ -334,9 +332,9 @@ those manually. Finds GTFS service_ids that have the exact same set of active days and consolidates each set of duplicated ids to a single service_id entry. -+---+ +``` {"op":"deduplicate_service_ids"} -+---+ +``` * Merge Trips and Simplify Calendar Entries @@ -349,9 +347,9 @@ as trips and stop times are duplicated. We provide a simple transformer that can attempt to detect these duplicate trips, remove them, and simplify the underlying calendar entries to match. To run it, apply the following transform: -+---+ +``` {"op":"calendar_simplification"} -+---+ +``` The transform takes additional optional arguments to control its behavior: @@ -372,7 +370,7 @@ calendar entries to match. To run it, apply the following transform: * undo_google_transit_data_feed_merge_tool - set to true to indicate that merged trip ids, as produced by the {{{http://code.google.com/p/googletransitdatafeed/wiki/Merge}GoogleTransitDataFeedMergeTool}}, should be un-mangled where possible. Merged trip ids will often have the form - <<>>. We attempt to set the trip id back to <<>> + `OriginalTripId_merged_1234567`. We attempt to set the trip id back to `OrginalTripId` where appropriate. [] @@ -389,9 +387,9 @@ such that the resulting schedule is semantically the same. To run it, apply the following transform: -+---+ +``` {"op":"shift_negative_stop_times"} -+---+ +``` <> When writing negative stop times, the negative value ONLY applies to the hour portion of the time. Here are a few examples: @@ -407,38 +405,38 @@ such that the resulting schedule is semantically the same. To remove them, apply the following transform: -+---+ +``` {"op":"remove_non_revenue_stops"} -+---+ +``` Terminals (the first and last stop_time of a trip) can be excluded from removal with the following transform: -+---+ +``` {"op":"remove_non_revenue_stops_excluding_terminals"}} -+---+ +``` * Replacing trip_headsign with the last stop Certain feeds contain unhelpful or incorrect trip_headsign. They can be replaced with the last stop's stop_name. -+---+ +``` {"op":"last_stop_to_headsign"} -+---+ +``` * Arbitrary Transform We also allow you to specify arbitrary transformations as well. Here, you specify your transformation class and we will automatically instantiate it for use in the transform pipeline. -+---+ +``` {"op":"transform", "class":"some.class.implementing.GtfsTranformStrategy"} -+---+ +``` We additionally provide a mechanism for setting additional properties of the transform. For all additional properties specified in the JSON snippet, we will attempt to set that Java bean property value on the instantiated transformation object. See for example: -+---+ +``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.updates.ShapeTransformStrategy", "shape_id":"6010031", \ "shape":"wjb~G|abmVpAz]v_@@?wNE_GDaFs@?@dFX`GGjN__@A"} +--+ @@ -453,18 +451,18 @@ Additional Examples We can apply a modification that retains certain GTFS entities and all other entities required directly or indirectly by those entities. For example, create a file with the following contents (call it modifications.txt, as an example): -+---+ +``` {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"B15"}} {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"B62"}} {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"B63"}} {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"BX19"}} {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"Q54"}} {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"S53"}} -+---+ +``` Then run: -+---+ +``` java -jar onebusaway-gtfs-transformer-cli.jar --transform=modifications.txt source-gtfs.zip target-gtfs.zip +--+ @@ -476,7 +474,7 @@ to support those routes. Consider an existing feed with a number of routes and stops. We can add an entirely new route, with trips and stop-times and frequency-based service, using the transform. This can be handy to add temporary service to an existing feed. -+---+ +``` {"op":"add", "obj":{"file":"routes.txt", "route_id":"r0", "route_long_name":"Temporary Shuttle", "route_type":3}} {"op":"add", "obj":{"file":"calendar.txt", "service_id":"WEEKDAY", "start_date":"20120601", "end_date":"20130630", "monday":1, "tuesday":1, "wednesday":1, "thursday":1, "friday":1}} @@ -489,6 +487,6 @@ and frequency-based service, using the transform. This can be handy to add temp {"op":"stop_times_factory", "trip_id":"t0", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["s0", "s1", "s2", "s3"]} {"op":"stop_times_factory", "trip_id":"t1", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["s3", "s2", "s1", "s0"]} -+---+ +``` diff --git a/pom.xml b/pom.xml index 2d248b2b4..5c8e10b87 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,6 @@ central true - published diff --git a/src/site/apt/release-notes.apt.vm b/src/site/apt/release-notes.apt.vm deleted file mode 100644 index 1cde88f3d..000000000 --- a/src/site/apt/release-notes.apt.vm +++ /dev/null @@ -1,223 +0,0 @@ -Release Notes - -* ${currentVersion} - - * Support for "extensions", allowing users of the library to add new GTFS fields for existing files, for reading and writing, without - needing to modify OneBusAway source code. For more details, see {{{./index.html#Reading_Custom_Fields}Reading Custom Fields}}. - - * Support for the proposed <<>> field in <<>>. - - * Support for the <<>> field in <<>>. - - * Bug fix for default-value <<>> field in <<>> - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/46}issue}} - - * Bug fix to ensure fare_attributes.txt transfers column is always included in GTFS output - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/50}issue}} - - * Support for "<<>>" expressions in GTFS transformer <<>> ops - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/48}issue}} - - * Support for "<<>>" expressions in GTFS transformer <<>> ops - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/52}issue}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/${currentVersion}/}} - -* 1.3.3 - - * Support for the proposed <<>> field in <<>>. - - * Support for <<>> field in both <<>> and <<>>, - with 0 = undefined, 1 = bikes allowed, 2 = bikes NOT allowed. Matches - the wheel-chair accessibility semantics. - - * New transformer to simply <<>> and <<>> entries, - combining service_ids that resolve to the exact same set of service - dates. - - * New transform strategy to fix shapes which are used for the wrong direction of travel. - - * Fix to properly handle using the library in non-English locales. {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/29}issue}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.3.3/}} - -* 1.3.2 - - * Introduce <<>>, a utility for loading GTFS into a database - {{{./onebusaway-gtfs-hibernate-cli.html}details}} - - * Support for <<>> <<>> field - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/pull/27}issue}} - - * Support for negative arrival and departure times in <<>>, along with a "shift_negative_stop_times" GTFS transformer - operation for normalizing feeds with negative stop-times. - - * Fix workaround for Java timezone bug - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/pull/28}issue}} - - * Introduce <<>>, a GTFS transformer strategy - {{{./apidocs/org/onebusaway/gtfs_transformer/updates/RemoveRepeatedStopTimesInSameTripStrategy.html}javadoc}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.3.2/}} - -* 1.3.1 - - * Support for <<>> <<>> field - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/21}issue}} - - * Support trip-2-trip transfers extension to transfers.txt - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/24}issue}} - - * Reduced memory consumption for ShapePoints and StopTimes - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/7}issue}} - - * Always include <<>> and <<>> in <<>> output - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/12}issue}} - - * onebusaway-gtfs-transformer-cli: - - * Use GTFS file and field names instead of OBA Java object and property names in matchers and updaters. - - * Support for "any(...)" matches. - - * New transforms: "trim_trip", "stop_times_factory", "calendar_extension", and "calendar_simplification". - - * onebusaway-gtfs-merge-cli: - - * Introduce a new OneBusAway GTFS merge tool for combining GTFS feeds. - - * Bug fixes: - - * NPE in ServiceDateUserType - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/9}issue}} - - * Better handling of <<>> agency resolution - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/18}issue}} - - * Crash with calendar simplification transform for <<>> with no active dates - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/20}issue}} - - * More useful file name when throwing CsvEntityIOException -{{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/23}issue}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.3.1/}} - -* 1.3.0 - - * Support for more natural field order in header when writing CSV file - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/2}issue}} - - * Support for excluding optional columns when no values are specified - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/3}issue}} - - * Fix for Daylight Saving Time service calendar computation bug - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/1}issue}} - - * Fix for bogus-timezone bug - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/issues/6}issue}} - - * Support for week-long stop_times.txt values - {{{https://github.com/OneBusAway/onebusaway-gtfs-modules/pull/5}issue}} - - * More flexible CSV parsing support from {{${site_base_url}/onebusaway-csv-entities/1.1.0/}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.3.0/}} - -* 1.2.6 - - * Migrate to GitHub. - - * When doing GTFS graph retention, allow the agency id referenced by stops, shapes, and service ids to refer to a - non-existent agency. These elements don't technically require an agency in the same way routes do, so it's ok if - they have a non-existent reference. - - * Add a custom field mapping for stop_lat and stop_lon in stops.txt that will better serialize their values when - writing back to the output file. We ran into an issue when working with stop locations very close to the prime - meridian where the stop_lon value was serialized using scientific notation. The new field mapping introduces a - custom formatter that will enforce a more normal decimal representation when the value is written to the output - file. - - * Allow input of multiple GTFS files to GTFS importer. - - * Better support for missing values when reading entity ids in the CSV-to-Object serialization library. - - * Support for frequencies.txt label_only field. - - * Better support for injecting new entities with the GtfsTransformer. - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.6/}} - -* 1.2.5 - - * Support for {{{https://developers.google.com/transit/gtfs/reference#feed_info_fields}feed_info.txt}} - - * Support for {{{https://developers.google.com/transit/gtfs/reference#agency_fields}agency.txt}} <<>> field - - * Support for {{{https://developers.google.com/transit/gtfs/reference#frequencies_fields}frequencies.txt}} <<>> field - - * Mark Hibernate GTFS classes as mutable (see {{{https://groups.google.com/group/onebusaway-developers/browse_thread/thread/219d40f7a99c9709}discussion}}) - - * Add <<>> method to {{{./apidocs/org/onebusaway/gtfs/services/GtfsRelationalDao.html#getStopsForStation}GtfsRelationalDao}} (see {{{https://groups.google.com/group/onebusaway-developers/browse_thread/thread/95f4335fcc0d056e}discussion}}) - - * Add <<>> method to {{{./apidocs/org/onebusaway/gtfs/model/calendar/CalendarServiceData.html#makeReadOnly()}CalendarServiceData}} - - * {{{./apidocs/org/onebusaway/gtfs/model/calendar/ServiceDate.html#parseString}ServiceDate.parseString()}} now throws a ParseException - - * Add {{{./apidocs/org/onebusaway/gtfs_transformer/updates/CalendarSimplicationStrategy.html}CalendarSimplicationStrategy}} for simplifying redundant trips / calendar entries - see {{{./onebusaway-gtfs-transformer-cli.html#Merge_Trips_and_Refactor_Calendar_Entries}usage instructions}} - - * Update {{{./apidocs/org/onebusaway/gtfs_transformer/updates/ShapeTransformStrategy.html}ShapeTransformStrategy}} to support updating just the start or end of a shape - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.5/}} - -* 1.2.4 - - * More documentation for {{{./onebusaway-gtfs-transformer-cli.html}onebusaway-gtfs-transformer-cli}}. - - * Add <<>> method to {{{./apidocs/org/onebusaway/gtfs/services/GtfsRelationalDao.html#getAllShapeIds()}GtfsRelationalDao}} - - * Add <<>> property to {{{./apidocs/org/onebusaway/gtfs/model/FareAttribute.html}FareAttribute}} - - * Better support for entity id matching in <<>>. - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.4/}} - -* 1.2.3 - - * More documentation for {{{./onebusaway-gtfs-transformer-cli.html}onebusaway-gtfs-transformer-cli}}. - - * Better support for entity id matching in <<>>. - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.3/}} - -* 1.2.2 - - * Add sorting of calendar.txt and calendar_dates.txt entries by service id when writing to output - - * Add additional manipulation methods to {{{./apidocs/org/onebusaway/gtfs/model/calendar/ServiceDate.html} ServiceDate}} - - * Initial entry for {{{./apidocs/org/onebusaway/gtfs/impl/GenericMutableDaoWrapper.html} GenericMutableDaoWrapper}} - - * Initial entry for {{{./apidocs/org/onebusaway/gtfs_transformer/services/GtfsEntityTransformStrategy.html} GtfsEntityTransformStrategy}} - - * Support in gtfs-transformer for the new entity tranformation strategy - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.2/}} - -* 1.2.1 - - * Support for {{{http://groups.google.com/group/gtfs-changes/browse_frm/thread/42a6863ae3661bba/d65149383c0d65e5?lnk=gst&q=bicycle#d65149383c0d65e5} bicycle accessibility}} proposal. - - * Support for {{{http://groups.google.com/group/gtfs-changes/browse_frm/thread/e6ef325c1ae92b6c} exact_times}} proposal. - - * Bump to {{{${site_base_url}/onebusaway-csv-entities/1.0.1/} onebusaway-csv-entities-1.0.1}}. - - * Add {{{./apidocs/org/onebusaway/gtfs/model/AgencyAndIdInstance.html} AgencyAndIdInstance}} class. - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.1/}} - -* 1.2.0 - - * Initial Site Documentation - - * Bump to require Maven 3 - - * Refactor CSV entity reading and writing support into its own module: {{${site_base_url}/onebusaway-csv-entities/1.0.0/}} - - * Full Documentation: {{${site_base_url}/onebusaway-gtfs-modules/1.2.0/}} - -* 1.1.11 - -* 1.1.10 - -* 1.1.9 - -* 1.1.8 - -* 1.1.7 - -* 1.1.6 - -* 1.1.5 - -* 1.1.4 - -* 1.1.3 From 3f2b5df06ddacea5c0a38da3e80e4d22e1b16cae Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Sep 2024 23:55:30 +0200 Subject: [PATCH 051/234] Convert more docs to markdown --- docs/index.md | 26 +-- docs/onebusaway-gtfs-hibernate-cli.md | 4 +- docs/onebusaway-gtfs-merge-cli.md | 4 +- docs/onebusaway-gtfs-transformer-cli.md | 208 +++++++++++------------- 4 files changed, 113 insertions(+), 129 deletions(-) diff --git a/docs/index.md b/docs/index.md index dae952433..3889defd7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,26 +1,26 @@ # onebusaway-gtfs-modules - We provide a Java library for reading and writing {{{https://developers.google.com/transit/gtfs} GTFS}} feeds, including database support. + We provide a Java library for reading and writing [ GTFS](https://developers.google.com/transit/gtfs) feeds, including database support. The library is broken up into a few key modules: * `onebusaway-gtfs` - The core library for reading and writing GTFS - * `onebusaway-gtfs-hibernate` - Support for {{{http://www.hibernate.org/}Hibernate}} database persistence of GTFS data + * `onebusaway-gtfs-hibernate` - Support for [Hibernate](http://www.hibernate.org/) database persistence of GTFS data - * `onebusaway-gtfs-hibernate-cli` - Command-line utilty for loading GTFS feeds into a database - see {{{./onebusaway-gtfs-hibernate-cli.html}the full documentation}}. + * `onebusaway-gtfs-hibernate-cli` - Command-line utilty for loading GTFS feeds into a database - see [the full documentation](./onebusaway-gtfs-hibernate-cli.html). * `onebusaway-gtfs-transformer` - Tools for transforming GTFS data - * `onebusaway-gtfs-transformer-cli` - Command-line utility for transforming GTFS - see {{{./onebusaway-gtfs-transformer-cli.html}the full documentation}}. + * `onebusaway-gtfs-transformer-cli` - Command-line utility for transforming GTFS - see [the full documentation](./onebusaway-gtfs-transformer-cli.html). * `onebusaway-gtfs-merge` - Tools for merging GTFS data - * `onebusaway-gtfs-merge-cli` - Command-line utility for merging GTFS feeds - see {{{./onebusaway-gtfs-merge-cli.html}the full documentation}}. + * `onebusaway-gtfs-merge-cli` - Command-line utility for merging GTFS feeds - see [the full documentation](./onebusaway-gtfs-merge-cli.html). Documentation - You can access the {{{./apidocs/index.html}latest Javadoc for the library}}. Also, see example source code below. + You can access the [latest Javadoc for the library](./apidocs/index.html). Also, see example source code below. Using in Maven @@ -50,7 +50,7 @@ Using in Maven ``` #if( $currentVersion.contains('SNAPSHOT') ) - To use a SNAPSHOT version of the library, you'll need to {{{https://github.com/OneBusAway/onebusaway/wiki/Maven-Repository}add a reference to the OneBusAway Maven repository}}. + To use a SNAPSHOT version of the library, you'll need to [add a reference to the OneBusAway Maven repository](https://github.com/OneBusAway/onebusaway/wiki/Maven-Repository). #end Example Code @@ -105,11 +105,11 @@ public class GtfsReaderExampleMain { } ``` - Notice that the {{{./apidocs/org/onebusaway/gtfs/serialization/GtfsReader.html}GtfsReader}} does the bulk of the work of reading the GTFS feed. The general pattern is to create the reader, set the input file, and call `run()` to start the reading process. You can manage the resulting GTFS entities in a couple of ways: + Notice that the [GtfsReader](./apidocs/org/onebusaway/gtfs/serialization/GtfsReader.html) does the bulk of the work of reading the GTFS feed. The general pattern is to create the reader, set the input file, and call `run()` to start the reading process. You can manage the resulting GTFS entities in a couple of ways: - * Register an {{{../../onebusaway-csv-entities/${onebusaway_csv_entities_version}/apidocs/org/onebusaway/csv_entities/EntityHandler.html}EntityHandler}} to handle entities as they are read + * Register an [EntityHandler](../../onebusaway-csv-entities/${onebusaway_csv_entities_version}/apidocs/org/onebusaway/csv_entities/EntityHandler.html) to handle entities as they are read - * Use an instance of {{{./apidocs/org/onebusaway/gtfs/services/GenericMutableDao.html}GenericMutableDao}} to examine the loaded entities after reading is complete + * Use an instance of [GenericMutableDao](./apidocs/org/onebusaway/gtfs/services/GenericMutableDao.html) to examine the loaded entities after reading is complete * Basic Writing @@ -247,7 +247,7 @@ stop_id,stop_name,stop_lat,stop_lon,extra_stop_info 123,Some Station,47.0,-122.0,This is a cool transit station ``` - The `extra_stop_info` field isn't included in the the {{{./apidocs/org/onebusaway/gtfs/model/Stop.html}Stop}} data + The `extra_stop_info` field isn't included in the the [Stop](./apidocs/org/onebusaway/gtfs/model/Stop.html) data model by default. So instead, we define a special `StopExtension` Java bean type with the new field: ``` @@ -280,6 +280,6 @@ System.out.println(extension.getExtraStopInfo()); ``` For more information on defining the mapping from GTFS fields to Java beans, see documentation for -the {{{https://github.com/OneBusAway/onebusaway-csv-entities}onebusaway-csv-entities}} project, -including the {{{../../onebusaway-csv-entities/${onebusaway_csv_entities_version}/apidocs/org/onebusaway/csv_entities/schema/annotations/CsvField.html}@CsvField}} +the [onebusaway-csv-entities](https://github.com/OneBusAway/onebusaway-csv-entities) project, +including the [@CsvField](../../onebusaway-csv-entities/${onebusaway_csv_entities_version}/apidocs/org/onebusaway/csv_entities/schema/annotations/CsvField.html) annotation documentation. \ No newline at end of file diff --git a/docs/onebusaway-gtfs-hibernate-cli.md b/docs/onebusaway-gtfs-hibernate-cli.md index b80a45ce3..4cf866f4f 100644 --- a/docs/onebusaway-gtfs-hibernate-cli.md +++ b/docs/onebusaway-gtfs-hibernate-cli.md @@ -7,7 +7,7 @@ GTFS Hibernate Command-Line Utility Introduction The `onebusaway-gtfs-hibernate-cli` command-line utility is a simple command-line tool for loading a -{{{https://developers.google.com/transit/gtfs}GTFS}} feed into a database. +[GTFS](https://developers.google.com/transit/gtfs) feed into a database. Getting the Application @@ -21,7 +21,7 @@ Getting the Application #set( $url = 'https://repo.camsys-apps.com/' + $repository + '/org/onebusaway/onebusaway-gtfs-hibernate-cli/' + ${currentVersion} + '/onebusaway-gtfs-hibernate-cli-' + ${currentVersion} + '.jar' ) - {{{${url}}onebusaway-gtfs-hibernate-cli-${currentVersion}.jar}} + [.jar](${url}}onebusaway-gtfs-hibernate-cli-${currentVersion) Using the Application diff --git a/docs/onebusaway-gtfs-merge-cli.md b/docs/onebusaway-gtfs-merge-cli.md index a8e545728..75e5061a1 100644 --- a/docs/onebusaway-gtfs-merge-cli.md +++ b/docs/onebusaway-gtfs-merge-cli.md @@ -9,7 +9,7 @@ Introduction <>: This tool is a work in progress! This documentation may not be up-to-date. The `onebusaway-gtfs-merge-cli` command-line application is a simple command-line tool for merging -{{{https://developers.google.com/transit/gtfs}GTFS}} feeds. +[GTFS](https://developers.google.com/transit/gtfs) feeds. Getting the Application @@ -23,7 +23,7 @@ Getting the Application #set( $url = 'https://repo.camsys-apps.com/' + $repository + '/org/onebusaway/onebusaway-gtfs-merge-cli/' + ${currentVersion} + '/onebusaway-gtfs-merge-cli-' + ${currentVersion} + '.jar' ) - {{{${url}}onebusaway-gtfs-merge-cli-${currentVersion}.jar}} + [.jar](${url}}onebusaway-gtfs-merge-cli-${currentVersion) Using the Application diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 9868e9414..751fc30b8 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -6,32 +6,22 @@ Brian Ferris 2011-08-17 ------ -Introduction +## Introduction - The `onebusaway-gtfs-transformer-cli` command-line application is a simple command-line tool for transforming -{{{https://developers.google.com/transit/gtfs}GTFS}} feeds. +The `onebusaway-gtfs-transformer-cli` command-line application is a simple command-line tool for transforming +[GTFS](https://developers.google.com/transit/gtfs) feeds. -Requirements +### Requirements * Java 11 or greater -Getting the Application +### Getting the Application +You can download the application here: - You can download the application here: - -#if( $currentVersion.endsWith("-SNAPSHOT")) - #set( $repository = 'snapshots' ) -#else - #set( $repository = 'releases' ) -#end - -#set( $url = 'https://repo.camsys-apps.com/' + $repository + '/org/onebusaway/onebusaway-gtfs-transformer-cli/' + ${currentVersion} + '/onebusaway-gtfs-transformer-cli-' + ${currentVersion} + '.jar' ) - - {{{${url}}onebusaway-gtfs-transformer-cli-${currentVersion}.jar}} -Using the Application +### Using the Application - To run the application: +To run the application: ``` java -jar onebusaway-gtfs-transformer-cli.jar [-args] input_gtfs_path ... output_gtfs_path @@ -43,31 +33,28 @@ java -jar onebusaway-gtfs-transformer-cli.jar [-args] input_gtfs_path ... output max amount of memory allocated to Java with an option like `-Xmx1G` or greater. Adding the `-server` argument if you are running the Oracle or OpenJDK can also increase performance. -* Arguments +### Arguments * `--transform=...` : specify a transformation to apply to the input GTFS feed (see syntax below) - * `--agencyId=id` : specify a default agency id for the input GTFS feed - * `--overwriteDuplicates` : specify that duplicate GTFS entities should overwrite each other when read - [] -* Transform Syntax +### Transform Syntax - Transforms are specified as snippets of example. A simple example to remove a stop might look like: +Transforms are specified as snippets of example. A simple example to remove a stop might look like: ``` {"op":"remove","match":{"file":"stops.txt","stop_name":"Stop NameP"}} ``` - You can pass those snippets to the application in a couple of ways. The simplest is directly on the command line. +You can pass those snippets to the application in a couple of ways. The simplest is directly on the command line. ``` --transform='{...}' ``` - You can have multiple `--transform` arguments to specify multiple transformations. However, if you have a LOT of +You can have multiple `--transform` arguments to specify multiple transformations. However, if you have a LOT of transformations that you wish to apply, it can be easier to put them in a file, with a JSON snippet per line. Then specify the file on the command-line: @@ -75,15 +62,15 @@ specify the file on the command-line: --transform=path/to/local-file ``` - You can even specify a URL where the transformations will be read: +You can even specify a URL where the transformations will be read: ``` --transform=http://server/path -+--+ +``` -Matching +### Matching - We provide a number of configurable transformations out-of-the-box that can do simple operations like adding, +We provide a number of configurable transformations out-of-the-box that can do simple operations like adding, updating, retaining, and removing GTFS entities. Many of the transforms accept a "`match`" term that controls how the rule matches against entities: @@ -91,15 +78,15 @@ rule matches against entities: {"op":..., "match":{"file":"routes.txt", "route_short_name":"574"}} ``` - Here, the match snippet at minimum requires a `file` property that specifies the type of GTFS entity to match. -Any file name defined in the {{{https://developers.google.com/transit/gtfs/reference#FeedFiles}GTFS specification}} +Here, the match snippet at minimum requires a `file` property that specifies the type of GTFS entity to match. +Any file name defined in the [GTFS specification](https://developers.google.com/transit/gtfs/reference#FeedFiles) can be used. - You can specify additional properties and values to match against as needed. Again, use the field names defined for +You can specify additional properties and values to match against as needed. Again, use the field names defined for each file name in the GTFS specification. For example, the snippet above will match any entry in `routes.txt` with a `route_short_name` value of `574`. -* Regular Expressions +### Regular Expressions Property matching also supports regular expressions that allow you to match property values conforming to a regexp pattern. For example, the snippet below will match any entry in `stops.txt` with a `stop_id` starting with `de:08`. @@ -107,7 +94,7 @@ each file name in the GTFS specification. For example, the snippet above will m {"op":..., "match":{"file":"stops.txt", "stop_id":"m/^de:08.*/"}} ``` -* Compound Property Expressions +### Compound Property Expressions Property matching also supports compound property expressions that allow you to match across GTFS relational references. Let's look at a simple example: @@ -116,7 +103,7 @@ references. Let's look at a simple example: {"op":..., "match":{"file":"trips.txt", "route.route_short_name":"10"}} ``` - Here the special `routes` property references the route entry associated with each trip, allowing you to match +Here the special `routes` property references the route entry associated with each trip, allowing you to match the properties of the route. You can even chain references, like `route.agency` to match against the agency associated with the trip. Here is the full list of supported compound property references: @@ -132,7 +119,7 @@ associated with the trip. Here is the full list of supported compound property {"op":..., "match":{"file":"fare_rules.txt", "route.route_short_name":"10"}} ``` -* Multi-Value Matches +### Multi-Value Matches The compound property expressions shown above are all for 1-to-1 relations, but matching also supports a limited form of multi-value matching for 1-to-N relations. Let's look at a simple example: @@ -141,7 +128,7 @@ form of multi-value matching for 1-to-N relations. Let's look at a simple examp {"op":..., "match":{"file":"routes.txt", "any(trips.trip_headsign)":"Downtown"}} ``` - Notice the addition of `any(...)` around the property name. Here we are using a special `trips` property that +Notice the addition of `any(...)` around the property name. Here we are using a special `trips` property that expands to include all trips associated with each route. Now, if *any* trip belonging to the route has the specified `trip_headsign` value, then the route matches. Here is the full list of supported multi-value property matches: @@ -151,9 +138,9 @@ expands to include all trips associated with each route. Now, if *any* trip bel {"op":..., "match":{"file":"trips.txt", "any(stop_times.X)":"Y"}} ``` -* Collection-Like Entities +### Collection-Like Entities - There are a number of GTFS entites that are more effectively collections identified by a common key. For example, +There are a number of GTFS entites that are more effectively collections identified by a common key. For example, shape points in `shapes.txt` linked by a common `shape_id` value or `calendar.txt` and `calendar_dates.txt` entries linked by a common `service_id` value. You can use a special `collection `match clause to match against the entire collection. @@ -163,137 +150,136 @@ collection. {"op":..., "match":{"collection":"calendar", "service_id":"XYZ"}} ``` - You can use the calendar collection matches, for example, to retain a calendar, including all `calendar.txt`, +You can use the calendar collection matches, for example, to retain a calendar, including all `calendar.txt`, `calendar_dates.txt`, and `trip.txt` entries that reference the specified `service_id` value. This convenient short-hand is easier than writing the equivalent expression using references to the three file types separately. -Types of Transforms +### Types of Transforms -* Add an Entity +#### Add an Entity - Create and add a new entity to the feed. +Create and add a new entity to the feed. ``` {"op":"add","obj":{"file":"agency.txt", "agency_id":"ST", "agency_name":"Sound Transit", "agency_url":"http://www.soundtransit.org", "agency_timezone":"America/Los_Angeles"}} ``` -* Update an Entity +#### Update an Entity - You can update arbitrary fields of a GTFS entity. +You can update arbitrary fields of a GTFS entity. ``` {"op":"update", "match":{"file":"routes.txt", "route_short_name":"574"}, "update":{"agency_id":"ST"}} ``` - Normally, update values are used as-is. However, we support a number of - special update operations: +Normally, update values are used as-is. However, we support a number of +special update operations: -** Find/Replace +### Find/Replace ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} ``` - By using `s/.../.../` syntax in the update value, the upda```te will perform - a find-replace operation on the specified property value. Consider the - following example: +By using `s/.../.../` syntax in the update value, the upda```te will perform +a find-replace operation on the specified property value. Consider the +following example: ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} ``` - Here, a trip with a headsign of `North Seattle` will be updated to - `N Seattle`. +Here, a trip with a headsign of `North Seattle` will be updated to `N Seattle`. -** Path Expressions +### Path Expressions - By using `path(...)` syntax in the update value, the expression will be - treated as a compound Java bean properties path expression. This path - expression will be evaluated against the target entity to produce the update - value. Consider the following example: +By using `path(...)` syntax in the update value, the expression will be +treated as a compound Java bean properties path expression. This path +expression will be evaluated against the target entity to produce the update +value. Consider the following example: ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"path(route.longName)"}} ``` - Here, the `trip_short_name` field is updated for each trip in the feed. - The value will be copied from the `route_long_name` field of each trip's - associated route. +Here, the `trip_short_name` field is updated for each trip in the feed. +The value will be copied from the `route_long_name` field of each trip's +associated route. -* Retain an Entity +### Retain an Entity - We also provide a powerful mechanism for selecting just a sub-set of a feed. - You can apply retain operations to entities you wish to keep and all the supporting entities referenced - by the retained entity will be retained as well. Unreferenced entities will be pruned. - - In the following example, only route B15 will be retained, along with all the stops, trips, stop times, shapes, and agencies linked to directly by that route. +We also provide a powerful mechanism for selecting just a sub-set of a feed. +You can apply retain operations to entities you wish to keep and all the supporting entities referenced +by the retained entity will be retained as well. Unreferenced entities will be pruned. + +In the following example, only route B15 will be retained, along with all the stops, trips, stop times, shapes, and agencies linked to directly by that route. ``` {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"B15"}} ``` - By default, we retain across {{{https://developers.google.com/transit/gtfs/reference#trips_block_id_field}block_id}} values +By default, we retain across [block_id](https://developers.google.com/transit/gtfs/reference#trips_block_id_field) values specified in trips.txt. That means if a particular trip is retained (perhaps because its parent route is retained), and the trip specifies a block_id, then all the trips referencing that block_id will be retained as well, along with their own routes, stop times, and shapes. This can potentially lead to unexpected results if you retain one route and suddenly see other routes included because they are linked by block_id. - You can disable this feature by specifying `retainBlocks: false` in the JSON transformer snippet. Here is an +You can disable this feature by specifying `retainBlocks: false` in the JSON transformer snippet. Here is an example: ``` {"op":"retain","match":{"file":"routes.txt", "route_short_name":"B15"}, "retainBlocks":false} ``` -* Remove an Entity +### Remove an Entity - You can remove a specific entity from a feed. +You can remove a specific entity from a feed. ``` {"op":"remove", "match":{"file":"stops.txt", "stop_name":"Stop Name"}} ``` - Note that removing an entity has a cascading effect. If you remove a trip, all the stop times that depend on that +Note that removing an entity has a cascading effect. If you remove a trip, all the stop times that depend on that trip will also be removed. If you remove a route, all the trips and stop times for that route will be removed. -* Trim a Trip +### Trim a Trip - You can remove stop times from the beginning or end of a trip using the "trim_trip" operation. Example: +You can remove stop times from the beginning or end of a trip using the "trim_trip" operation. Example: ``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "from_stop_id":"138S"} ``` - For any trip belonging to the specified route and passing through the specified stop, all stop times from the specified +For any trip belonging to the specified route and passing through the specified stop, all stop times from the specified stop onward will be removed from the trip. You can also remove stop times from the beginning of the trip as well: ``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "to_stop_id":"138S"} ``` - Or both: +Or both: ``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "to_stop_id":"125S", "from_stop_id":"138S"} ``` -* Generate Stop Times +### Generate Stop Times - You can generate stop time entries for a trip. Example: +You can generate stop time entries for a trip. Example: ``` {"op":"stop_times_factory", "trip_id":"TRIP01", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["S01", "S02", "S03"]} ``` - A series of entries in `stop_times.txt` will be generated for the specified trip, traveling along the specified sequence of +A series of entries in `stop_times.txt` will be generated for the specified trip, traveling along the specified sequence of stops. The departure time for the first stop will be set from the `start_time` field, the arrival time for the last stop will be set from the `end_time` field, and the times for intermediate stops will be interpolated based on their distance along the trip. -* Extend Service Calendars +### Extend Service Calendars - Sometimes you need to extend the service dates in a GTFS feed, perhaps in order to temporarily extend an expired feed. Extending +Sometimes you need to extend the service dates in a GTFS feed, perhaps in order to temporarily extend an expired feed. Extending the feed by hand can be a tedious task, especially when the feed uses a complex combination of `calendar.txt` and `calendar_dates.txt` entries. Fortunately, the GTFS tranformer tool supports a `calendar_extension` operation that can help simplify the work. Example: @@ -301,16 +287,16 @@ entries. Fortunately, the GTFS tranformer tool supports a `calendar_extension` {"op":"calendar_extension", "end_date":"20130331"} ``` - The operation requires just one argument by default: `end_date` to specify the new end-date for the feed. The operation does +The operation requires just one argument by default: `end_date` to specify the new end-date for the feed. The operation does its best to intelligently extend each service calendar, as identified by a `service_id` in `calendar.txt` or `calendar_dates.txt`. There are a few wrinkles to be aware of, however. - Extending a `calendar.txt` entry is usually just a matter of setting a new `end_date` value in the feed. Extending a service +Extending a `calendar.txt` entry is usually just a matter of setting a new `end_date` value in the feed. Extending a service calendar represented only through `calendar_dates.txt` entries is a bit more complex. For such a service calendar, we attempt to determine which days of the week are typically active for the calendar and extend only those. For example, is the calendar is always active on Saturday but has one or two Sunday entries, we will only add entries for Saturday when extending the calendar. - Also note that we will not extend "inactive" service calendars. A service calendar is considered inactive if its last active +Also note that we will not extend "inactive" service calendars. A service calendar is considered inactive if its last active service date is already in the past. By default, any calendar that's been expired for more than two weeks is considered inactive. This helps handle feeds that have merged two service periods in one feed. For example, one calendar active from June 1 - July 31 and a second calendar active from August 1 to September 31. If it's the last week of September and you are extending the feed, @@ -321,37 +307,36 @@ argument: {"op":"calendar_extension", "end_date":"20130331", "inactive_calendar_cutoff":"20121031"} ``` - Calendars that have expired before the specified date will be considered inactive and won't be extended. +Calendars that have expired before the specified date will be considered inactive and won't be extended. - <>: We don't make any effort to extend canceled service dates, as specified in `calendar_dates.txt` for holidays and +_Note_: We don't make any effort to extend canceled service dates, as specified in `calendar_dates.txt` for holidays and other special events. It's too tricky to automatically determine how they should be handled. You may need to still handle those manually. -* Deduplicate Calendar Entries +### Deduplicate Calendar Entries - Finds GTFS service_ids that have the exact same set of active days and consolidates each set of duplicated +Finds GTFS service_ids that have the exact same set of active days and consolidates each set of duplicated ids to a single service_id entry. ``` {"op":"deduplicate_service_ids"} ``` - -* Merge Trips and Simplify Calendar Entries +### Merge Trips and Simplify Calendar Entries - Some agencies model their transit schedule favoring multiple entries in calendar_dates.txt as opposed to a more concise +Some agencies model their transit schedule favoring multiple entries in calendar_dates.txt as opposed to a more concise entry in calendar.txt. A smaller number of agencies take this scheme even further, creating trips.txt entries for each service date, even when the underlying trips are exactly the same. This can cause the size of the GTFS to grow dramatically as trips and stop times are duplicated. - We provide a simple transformer that can attempt to detect these duplicate trips, remove them, and simplify the underlying +We provide a simple transformer that can attempt to detect these duplicate trips, remove them, and simplify the underlying calendar entries to match. To run it, apply the following transform: ``` {"op":"calendar_simplification"} ``` - The transform takes additional optional arguments to control its behavior: +The transform takes additional optional arguments to control its behavior: * min_number_of_weeks_for_calendar_entry - how many weeks does a service id need to span before it gets its own entry in calendar.txt (default=3) @@ -368,30 +353,29 @@ calendar entries to match. To run it, apply the following transform: (default=0.5) * undo_google_transit_data_feed_merge_tool - set to true to indicate that merged trip ids, - as produced by the {{{http://code.google.com/p/googletransitdatafeed/wiki/Merge}GoogleTransitDataFeedMergeTool}}, + as produced by the [GoogleTransitDataFeedMergeTool](http://code.google.com/p/googletransitdatafeed/wiki/Merge), should be un-mangled where possible. Merged trip ids will often have the form `OriginalTripId_merged_1234567`. We attempt to set the trip id back to `OrginalTripId` where appropriate. - [] -* Shift Negative Stop Times +### Shift Negative Stop Times - Some agencies have trips that they model as starting BEFORE midnight on a given service date. For these agencies, it +Some agencies have trips that they model as starting BEFORE midnight on a given service date. For these agencies, it would be convenient to represent these trips with negative arrival and departure times in stop_times.txt. The GTFS spec and many GTFS consumers do not support negative stop times, however. - To help these agencies, we provide a transform to "fix" GTFS feeds with negative stop times by identifying such trips, +To help these agencies, we provide a transform to "fix" GTFS feeds with negative stop times by identifying such trips, shifting the arrival and departure times to make them positive, and updating the service calendar entries for these trips such that the resulting schedule is semantically the same. - To run it, apply the following transform: +To run it, apply the following transform: ``` {"op":"shift_negative_stop_times"} ``` - <> When writing negative stop times, the negative value ONLY applies to the hour portion +_A note on negative stop times:_ When writing negative stop times, the negative value ONLY applies to the hour portion of the time. Here are a few examples: * "-01:45:00" => "23:45:00" on the previous day @@ -423,32 +407,32 @@ such that the resulting schedule is semantically the same. {"op":"last_stop_to_headsign"} ``` -* Arbitrary Transform +### Arbitrary Transform - We also allow you to specify arbitrary transformations as well. Here, you specify your transformation class and we will +We also allow you to specify arbitrary transformations as well. Here, you specify your transformation class and we will automatically instantiate it for use in the transform pipeline. ``` {"op":"transform", "class":"some.class.implementing.GtfsTranformStrategy"} ``` - We additionally provide a mechanism for setting additional properties of the transform. For all additional properties +We additionally provide a mechanism for setting additional properties of the transform. For all additional properties specified in the JSON snippet, we will attempt to set that Java bean property value on the instantiated transformation object. See for example: ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.updates.ShapeTransformStrategy", "shape_id":"6010031", \ "shape":"wjb~G|abmVpAz]v_@@?wNE_GDaFs@?@dFX`GGjN__@A"} -+--+ +``` - Here, we set additional properties on the `ShapeTransformStrategy`, making it possible to reuse and configure a generic +Here, we set additional properties on the `ShapeTransformStrategy`, making it possible to reuse and configure a generic transformer to your needs. Additional Examples -* How to Reduce your GTFS +### How to Reduce your GTFS - We can apply a modification that retains certain GTFS entities and all other entities required directly or indirectly by +We can apply a modification that retains certain GTFS entities and all other entities required directly or indirectly by those entities. For example, create a file with the following contents (call it modifications.txt, as an example): ``` @@ -460,18 +444,18 @@ those entities. For example, create a file with the following contents (call it {"op":"retain", "match":{"file":"routes.txt", "route_short_name":"S53"}} ``` - Then run: +Then run: ``` java -jar onebusaway-gtfs-transformer-cli.jar --transform=modifications.txt source-gtfs.zip target-gtfs.zip -+--+ +``` - The resulting GTFS will have the retained only the routes with the matching short names and all other entities required +The resulting GTFS will have the retained only the routes with the matching short names and all other entities required to support those routes. * Add a Full Schedule to an Existing Feed - Consider an existing feed with a number of routes and stops. We can add an entirely new route, with trips and stop-times +Consider an existing feed with a number of routes and stops. We can add an entirely new route, with trips and stop-times and frequency-based service, using the transform. This can be handy to add temporary service to an existing feed. ``` From ea847e5eeb13b3d27123516dda20550d25624fa5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:04:51 +0200 Subject: [PATCH 052/234] Update docs --- README.md | 23 ++++++++--------------- docs/onebusaway-gtfs-merge-cli.md | 6 +----- docs/onebusaway-gtfs-transformer-cli.md | 12 ++++-------- 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 3bfe716cf..a6f0c1ea0 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,23 @@ -# onebusaway-gtfs-modules [![CI](https://github.com/OneBusAway/onebusaway-gtfs-modules/actions/workflows/ci.yml/badge.svg)](https://github.com/OneBusAway/onebusaway-gtfs-modules/actions/workflows/ci.yml) +# onebusaway-gtfs-modules + +[![CI](https://github.com/OneBusAway/onebusaway-gtfs-modules/actions/workflows/ci.yml/badge.svg)](https://github.com/OneBusAway/onebusaway-gtfs-modules/actions/workflows/ci.yml) +[![Maven Central](https://img.shields.io/maven-central/v/org.onebusaway/onebusaway-gtfs-modules.svg)](https://mvnrepository.com/artifact/org.onebusaway/onebusaway-gtfs-modules) A Java library for reading and writing [GTFS](https://developers.google.com/transit/gtfs) feeds, including database support. -See more documentation [on the wiki](https://github.com/OneBusAway/onebusaway-gtfs-modules/wiki). +See more documentation in the [`docs folder`](./docs). ## Maven usage In your `pom.xml`, include: -~~~ - - - public.onebusaway.org - https://repo.camsys-apps.com/releases/ - - -~~~ - -... and inside ``: +``` -~~~ org.onebusaway onebusaway-gtfs - 1.3.88 + 2.0.0 -~~~ +``` ...where `` contains the latest version number. diff --git a/docs/onebusaway-gtfs-merge-cli.md b/docs/onebusaway-gtfs-merge-cli.md index 75e5061a1..66289a908 100644 --- a/docs/onebusaway-gtfs-merge-cli.md +++ b/docs/onebusaway-gtfs-merge-cli.md @@ -1,8 +1,4 @@ - ------ -GTFS Merge Command-Line Application - ------ - ------ - ------ +# GTFS Merge Command-Line Application Introduction diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 751fc30b8..65f43f544 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -1,10 +1,4 @@ - ------ -GTFS Transformation Command-Line Application - ------ -Brian Ferris - ------ -2011-08-17 - ------ +# GTFS Transformation Command-Line Application ## Introduction @@ -16,8 +10,10 @@ The `onebusaway-gtfs-transformer-cli` command-line application is a simple comma * Java 11 or greater ### Getting the Application -You can download the application here: +You can download the application from Maven Central: https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-transformer-cli/ + +Select the largest jar file from the version you would like to use, for example https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-transformer-cli/2.0.0/onebusaway-gtfs-transformer-cli-2.0.0.jar ### Using the Application From 2e38fd5dcc31871bdfeff0f9db4574b95b56a531 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:07:46 +0200 Subject: [PATCH 053/234] Remove maven site plugin --- onebusaway-gtfs-hibernate/src/site/site.xml | 6 ---- onebusaway-gtfs-merge-cli/src/site/site.xml | 6 ---- onebusaway-gtfs-merge/src/site/site.xml | 6 ---- .../src/site/site.xml | 6 ---- onebusaway-gtfs-transformer/src/site/site.xml | 6 ---- onebusaway-gtfs/src/site/site.xml | 6 ---- pom.xml | 29 ------------------- src/site/site.xml | 24 --------------- 8 files changed, 89 deletions(-) delete mode 100644 onebusaway-gtfs-hibernate/src/site/site.xml delete mode 100644 onebusaway-gtfs-merge-cli/src/site/site.xml delete mode 100644 onebusaway-gtfs-merge/src/site/site.xml delete mode 100644 onebusaway-gtfs-transformer-cli/src/site/site.xml delete mode 100644 onebusaway-gtfs-transformer/src/site/site.xml delete mode 100644 onebusaway-gtfs/src/site/site.xml delete mode 100644 src/site/site.xml diff --git a/onebusaway-gtfs-hibernate/src/site/site.xml b/onebusaway-gtfs-hibernate/src/site/site.xml deleted file mode 100644 index 80c56bc38..000000000 --- a/onebusaway-gtfs-hibernate/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/onebusaway-gtfs-merge-cli/src/site/site.xml b/onebusaway-gtfs-merge-cli/src/site/site.xml deleted file mode 100644 index edd5d57d6..000000000 --- a/onebusaway-gtfs-merge-cli/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/onebusaway-gtfs-merge/src/site/site.xml b/onebusaway-gtfs-merge/src/site/site.xml deleted file mode 100644 index 8cec22b54..000000000 --- a/onebusaway-gtfs-merge/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/onebusaway-gtfs-transformer-cli/src/site/site.xml b/onebusaway-gtfs-transformer-cli/src/site/site.xml deleted file mode 100644 index 2cc85caf3..000000000 --- a/onebusaway-gtfs-transformer-cli/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/onebusaway-gtfs-transformer/src/site/site.xml b/onebusaway-gtfs-transformer/src/site/site.xml deleted file mode 100644 index 323e55b50..000000000 --- a/onebusaway-gtfs-transformer/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/onebusaway-gtfs/src/site/site.xml b/onebusaway-gtfs/src/site/site.xml deleted file mode 100644 index 99a22b504..000000000 --- a/onebusaway-gtfs/src/site/site.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/pom.xml b/pom.xml index 5c8e10b87..01c94cc83 100644 --- a/pom.xml +++ b/pom.xml @@ -66,12 +66,6 @@ https://repo.camsys-apps.com/snapshots/ - - - ${site_id} - ${site_deployment_base}/onebusaway-gtfs-modules/${project.version} - - onebusaway-gtfs @@ -153,24 +147,6 @@ - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.4.2 - - - - index - licenses - - - - - - - @@ -182,11 +158,6 @@ 11 - - org.apache.maven.plugins - maven-site-plugin - 3.12.1 - org.sonatype.central central-publishing-maven-plugin diff --git a/src/site/site.xml b/src/site/site.xml deleted file mode 100644 index 05d5ad552..000000000 --- a/src/site/site.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - org.apache.maven.skins - maven-fluido-skin - 1.11.1 - - From 60d8828312732aa567227df5664596bf45a983b6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:10:12 +0200 Subject: [PATCH 054/234] Remove site deployment --- .github/workflows/ci.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40fe1498c..8b48472fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,14 +20,4 @@ jobs: cache: 'maven' - name: Test project with Maven - run: mvn --no-transfer-progress test package - -# - name: Build documentation -# run: mvn --no-transfer-progress site -# -# - name: Deploy documentation to Github Pages -# # only deploy after merging to master -# if: github.repository_owner == 'OneBusAway' && github.event_name == 'push' && github.ref == 'refs/heads/master' -# uses: JamesIves/github-pages-deploy-action@v4 -# with: -# folder: target/site/ + run: mvn --no-transfer-progress test package \ No newline at end of file From 6e63518cc640164b25ed366f43088b2bb5cc3dcf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:15:50 +0200 Subject: [PATCH 055/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-2.0.1 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 564ec254a..2bc5a7e47 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 53f97b313..f13616522 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 9343495d1..e812e7f72 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.1-SNAPSHOT + 2.0.1 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 0cd80835b..61bf4df8f 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.1-SNAPSHOT + 2.0.1 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 7d98c5197..52aa81505 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 2f852a6ef..76995ba28 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index f80135bfe..ec066434f 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 ../pom.xml diff --git a/pom.xml b/pom.xml index 01c94cc83..7462593ea 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1-SNAPSHOT + 2.0.1 pom onebusaway-gtfs-modules @@ -39,7 +39,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-2.0.1 From 236b72bb0fee44425f5c1193bbdc33a0fc1b8aba Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:15:52 +0200 Subject: [PATCH 056/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 2bc5a7e47..323dab6e8 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index f13616522..fc24cfcc8 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index e812e7f72..f48b5f6bd 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.1 + 2.0.2-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 61bf4df8f..ec2174cb8 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.1 + 2.0.2-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 52aa81505..66cf55376 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 76995ba28..366d7928d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index ec066434f..ef41a431f 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 7462593ea..cd09a67a5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.1 + 2.0.2-SNAPSHOT pom onebusaway-gtfs-modules @@ -39,7 +39,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-2.0.1 + HEAD From 29d34e7acc264c90f2c6ab4a2ff9089490a5deb2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:26:34 +0200 Subject: [PATCH 057/234] Add automatic release --- .github/workflows/release.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..03dc2d6b8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,18 @@ +name: Create Github release + +on: + push: + tags: + - "onebusaway-gtfs-modules-*.*.*" + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Release + uses: softprops/action-gh-release@v2 \ No newline at end of file From 21afdf012a0408b9cf90507f61023317d6a0a8bb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 00:27:29 +0200 Subject: [PATCH 058/234] Remove log4j config --- .github/workflows/release.yml | 2 +- .../src/main/resources/log4j.properties | 27 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 onebusaway-gtfs-transformer/src/main/resources/log4j.properties diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 03dc2d6b8..b70b94d96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: permissions: contents: write - + jobs: build: runs-on: ubuntu-latest diff --git a/onebusaway-gtfs-transformer/src/main/resources/log4j.properties b/onebusaway-gtfs-transformer/src/main/resources/log4j.properties deleted file mode 100644 index ddc38d6df..000000000 --- a/onebusaway-gtfs-transformer/src/main/resources/log4j.properties +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2008 Brian Ferris -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - - -log4j.rootLogger = INFO, stdout - -log4j.appender.stdout = org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Threshold = DEBUG -log4j.appender.stdout.Target = System.out -log4j.appender.stdout.layout = org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern = %d{ISO8601} %-5p [%F:%L] : %m%n - -#log4j.category.org.onebusaway=DEBUG - - - From 6c62c0e8ec56724a18f0abf050a1bfc9f3eba63e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 09:42:03 +0200 Subject: [PATCH 059/234] Clean up duplicate and unused configuration entries in POMs --- onebusaway-gtfs-merge-cli/pom.xml | 16 --- onebusaway-gtfs-merge/pom.xml | 1 + onebusaway-gtfs-transformer-cli-aws/pom.xml | 104 -------------------- onebusaway-gtfs-transformer/pom.xml | 14 --- onebusaway-gtfs/pom.xml | 1 + 5 files changed, 2 insertions(+), 134 deletions(-) delete mode 100644 onebusaway-gtfs-transformer-cli-aws/pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index f48b5f6bd..c9405293c 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -56,23 +56,7 @@ - - org.apache.maven.plugins - maven-deploy-plugin - - ${skip-deploy-onebusaway-gtfs-merge-cli-jar} - - - - - oss-distribution - - true - - - - diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index ec2174cb8..76d2cc0e8 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -26,6 +26,7 @@ org.slf4j slf4j-simple ${slf4j_version} + test org.junit.jupiter diff --git a/onebusaway-gtfs-transformer-cli-aws/pom.xml b/onebusaway-gtfs-transformer-cli-aws/pom.xml deleted file mode 100644 index 8975ed344..000000000 --- a/onebusaway-gtfs-transformer-cli-aws/pom.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - 4.0.0 - onebusaway-gtfs-transformer-cli-aws - - onebusaway-gtfs-transformer-cli-aws - A Java application for transforming Google Transit Feed Spec feeds with AWS extensions - - - org.onebusaway - onebusaway-gtfs-modules - 1.4.18-SNAPSHOT - - - - - false - - - - - org.onebusaway - onebusaway-gtfs-transformer-cli - ${project.version} - - - org.onebusaway - onebusaway-cloud-aws - ${onebusaway_cloud_version} - - - commons-cli - commons-cli - 1.2 - - - org.slf4j - slf4j-simple - ${slf4j_version} - - - org.junit.jupiter - junit-jupiter-api - test - - - - javax.xml.bind - jaxb-api - - - com.sun.xml.bind - jaxb-core - - - com.sun.xml.bind - jaxb-impl - - - - - onebusaway-gtfs-transformer-cli-aws - - - maven-shade-plugin - - - package - - shade - - - withAllDependencies - false - - - org.onebusaway.gtfs_transformer.GtfsTransformerMain - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - ${skip-deploy-onebusaway-gtfs-transformer-cli-jar} - - - - - - - - oss-distribution - - true - - - - - diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 366d7928d..20e6d49c1 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -77,18 +77,4 @@ - - onebusaway-gtfs-transformer - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - - - diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index ef41a431f..f67389e3b 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -43,6 +43,7 @@ org.slf4j slf4j-simple ${slf4j_version} + test org.junit.jupiter From eaefa3fa747154ecfa9c1ec9e345bfb16cac4a68 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 09:53:29 +0200 Subject: [PATCH 060/234] Remove duplicate test dependency --- onebusaway-gtfs-hibernate-cli/pom.xml | 5 +++-- onebusaway-gtfs-hibernate/pom.xml | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 323dab6e8..9f61f4d14 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.onebusaway @@ -36,7 +37,7 @@ - + maven-shade-plugin diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index fc24cfcc8..eb25b248d 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -37,15 +37,11 @@ mysql-connector-j 8.2.0 - - org.junit.jupiter - junit-jupiter-api - test - org.slf4j slf4j-simple ${slf4j_version} + test com.sun.xml.bind From 9e06e8aef800ab8b87954cca973da7e726d8adb5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 09:54:03 +0200 Subject: [PATCH 061/234] Remove unneeded config --- onebusaway-gtfs-hibernate-cli/pom.xml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 9f61f4d14..e8977ba5c 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -11,12 +11,6 @@ onebusaway-gtfs-hibernate-cli A command-line utility for loading GTFS data into a database. - - - false - - org.onebusaway @@ -57,22 +51,7 @@ - - org.apache.maven.plugins - maven-deploy-plugin - - ${skip-deploy-jar} - - - - - oss-distribution - - true - - - From 62c6615a0c86cac9300b89aa6a6df03fdd348d80 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 09:59:04 +0200 Subject: [PATCH 062/234] Clean up Junit definition --- onebusaway-gtfs-hibernate/pom.xml | 4 ++++ onebusaway-gtfs-merge/pom.xml | 1 - onebusaway-gtfs-transformer/pom.xml | 1 - onebusaway-gtfs/pom.xml | 1 - pom.xml | 3 ++- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index eb25b248d..23b79b463 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -43,6 +43,10 @@ ${slf4j_version} test + + org.junit.jupiter + junit-jupiter-api + com.sun.xml.bind jaxb-impl diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 76d2cc0e8..087226357 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -31,7 +31,6 @@ org.junit.jupiter junit-jupiter-api - test org.mockito diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 20e6d49c1..36f420eb9 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -36,7 +36,6 @@ org.junit.jupiter junit-jupiter-api - test org.slf4j diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index f67389e3b..79f53dbec 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -48,7 +48,6 @@ org.junit.jupiter junit-jupiter-api - test org.mockito diff --git a/pom.xml b/pom.xml index cd09a67a5..bc6679b3b 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ 0.0.13 UTF-8 UTF-8 + 5.11.0 @@ -120,7 +121,7 @@ org.junit.jupiter junit-jupiter-api - 5.6.2 + ${junit.version} test From 85d1bb55346e8b35369a569bda4eb9f5e8a76644 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 10:44:34 +0200 Subject: [PATCH 063/234] Clean up variables --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 3 ++- onebusaway-gtfs/pom.xml | 2 -- pom.xml | 13 ++++++------- 8 files changed, 13 insertions(+), 15 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index e8977ba5c..81ca63a30 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -25,7 +25,7 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 23b79b463..b99b8a8d0 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -40,7 +40,7 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} test diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index c9405293c..451dcbc6b 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -30,7 +30,7 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 087226357..f5da89651 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -25,7 +25,7 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} test diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 66cf55376..3dc4b136d 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -32,7 +32,7 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} org.junit.jupiter diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 36f420eb9..8266afa1e 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -40,7 +40,8 @@ org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} + test org.mockito diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 79f53dbec..f309c6e13 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -27,7 +27,6 @@ org.slf4j slf4j-api - ${slf4j_version} com.fasterxml.jackson.core @@ -42,7 +41,6 @@ org.slf4j slf4j-simple - ${slf4j_version} test diff --git a/pom.xml b/pom.xml index bc6679b3b..52dd56cfc 100644 --- a/pom.xml +++ b/pom.xml @@ -27,12 +27,12 @@ + UTF-8 + UTF-8 1.1.7 1.2.8 - 2.0.6 + 2.0.6 0.0.13 - UTF-8 - UTF-8 5.11.0 @@ -74,7 +74,6 @@ onebusaway-gtfs-hibernate-cli onebusaway-gtfs-transformer onebusaway-gtfs-transformer-cli - onebusaway-gtfs-merge onebusaway-gtfs-merge-cli @@ -106,17 +105,17 @@ org.slf4j slf4j - ${slf4j_version} + ${slf4j.version} org.slf4j slf4j-api - ${slf4j_version} + ${slf4j.version} org.slf4j slf4j-simple - ${slf4j_version} + ${slf4j.version} org.junit.jupiter From bdcb9f463f7327b6e81a2ae05965d912d1a98c96 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 10:53:55 +0200 Subject: [PATCH 064/234] Configure release plugin --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index 52dd56cfc..95018ff11 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,14 @@ + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + + + org.apache.maven.plugins maven-surefire-plugin From 84cefc2e695c8d5471db0a92a62fd94b8cca41c8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 10:59:15 +0200 Subject: [PATCH 065/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-2.0.2 --- onebusaway-gtfs-hibernate-cli/pom.xml | 5 ++--- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 6 +++--- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 81ca63a30..0049db23f 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index b99b8a8d0..6fca6769f 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 451dcbc6b..c817a253c 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.2-SNAPSHOT + 2.0.2 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index f5da89651..51a789427 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.2-SNAPSHOT + 2.0.2 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 3dc4b136d..0c557bac9 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 8266afa1e..54d67b987 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index f309c6e13..6b55d92e4 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 ../pom.xml diff --git a/pom.xml b/pom.xml index 95018ff11..41156ce64 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2-SNAPSHOT + 2.0.2 pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-2.0.2 @@ -217,7 +217,7 @@ maven-release-plugin 3.1.1 - + From 4aa3d2bcd81210ee5fe0c3a869b0c6d1a6412c27 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 10:59:16 +0200 Subject: [PATCH 066/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 0049db23f..d6f2d14d7 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 6fca6769f..5e0eafc1e 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index c817a253c..64d9f3ad5 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.2 + 2.0.3-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 51a789427..b009468ef 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.2 + 2.0.3-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 0c557bac9..29bcf0b99 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 54d67b987..7a9781f98 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 6b55d92e4..519bc889e 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 41156ce64..49d8a3c1d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.2 + 2.0.3-SNAPSHOT pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-2.0.2 + HEAD From c529a891526f3b67aef506c4c3bb5b3c5e7abddf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:08:13 +0200 Subject: [PATCH 067/234] Use correct syntax --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49d8a3c1d..bc54189a0 100644 --- a/pom.xml +++ b/pom.xml @@ -217,7 +217,7 @@ maven-release-plugin 3.1.1 - + true From 7271f7e2ad6263ae6645d6c293919a73b92efd7d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:10:31 +0200 Subject: [PATCH 068/234] Automatically generate release notes --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b70b94d96..0cf6ba0e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,4 +15,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Release - uses: softprops/action-gh-release@v2 \ No newline at end of file + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true \ No newline at end of file From 052249811a410ef99986d060630df3493686ce9c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:18:38 +0200 Subject: [PATCH 069/234] Upgrade Mockito --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bc54189a0..315a26973 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ org.mockito mockito-core - 1.8.0 + 5.12.0 test @@ -154,8 +154,8 @@ maven-compiler-plugin 3.10.1 - 11 - 11 + 17 + 17 From 65d874934f2adc6ebec169a0eea5fb3e87c6182d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:19:28 +0200 Subject: [PATCH 070/234] Upgrade CI Java version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b48472fa..1467afae9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Test project with Maven From 0984c253f277d06ad22df9a2ce23fdb2907e522e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:27:24 +0200 Subject: [PATCH 071/234] Update documentation --- docs/onebusaway-gtfs-transformer-cli.md | 58 +++++++++++++++++++------ 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 65f43f544..8bd625bf2 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -1,5 +1,35 @@ # GTFS Transformation Command-Line Application + +* [GTFS Transformation Command-Line Application](#gtfs-transformation-command-line-application) + * [Introduction](#introduction) + * [Requirements](#requirements) + * [Getting the Application](#getting-the-application) + * [Using the Application](#using-the-application) + * [Arguments](#arguments) + * [Transform Syntax](#transform-syntax) + * [Matching](#matching-) + * [Regular Expressions](#regular-expressions) + * [Compound Property Expressions](#compound-property-expressions) + * [Multi-Value Matches](#multi-value-matches) + * [Collection-Like Entities](#collection-like-entities) + * [Types of Transforms](#types-of-transforms) + * [Add an Entity](#add-an-entity) + * [Update an Entity](#update-an-entity) + * [Find/Replace](#findreplace) + * [Path Expressions](#path-expressions-) + * [Retain an Entity](#retain-an-entity) + * [Remove an Entity](#remove-an-entity) + * [Trim a Trip](#trim-a-trip) + * [Generate Stop Times](#generate-stop-times) + * [Extend Service Calendars](#extend-service-calendars) + * [Deduplicate Calendar Entries](#deduplicate-calendar-entries) + * [Merge Trips and Simplify Calendar Entries](#merge-trips-and-simplify-calendar-entries) + * [Shift Negative Stop Times](#shift-negative-stop-times) + * [Arbitrary Transform](#arbitrary-transform) + * [How to Reduce your GTFS](#how-to-reduce-your-gtfs) + + ## Introduction The `onebusaway-gtfs-transformer-cli` command-line application is a simple command-line tool for transforming @@ -7,7 +37,7 @@ The `onebusaway-gtfs-transformer-cli` command-line application is a simple comma ### Requirements - * Java 11 or greater + * Java 17 or greater ### Getting the Application @@ -25,7 +55,7 @@ java -jar onebusaway-gtfs-transformer-cli.jar [-args] input_gtfs_path ... output `input_gtfs_path` and `output_gtfs_path` can be either a directory containing a GTFS feed or a .zip file. - <>: Transforming large GTFS feeds is processor and memory intensive. You'll likely need to increase the +_Note_: Transforming large GTFS feeds is processor and memory intensive. You'll likely need to increase the max amount of memory allocated to Java with an option like `-Xmx1G` or greater. Adding the `-server` argument if you are running the Oracle or OpenJDK can also increase performance. @@ -172,7 +202,7 @@ You can update arbitrary fields of a GTFS entity. Normally, update values are used as-is. However, we support a number of special update operations: -### Find/Replace +#### Find/Replace ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} @@ -188,7 +218,7 @@ following example: Here, a trip with a headsign of `North Seattle` will be updated to `N Seattle`. -### Path Expressions +#### Path Expressions By using `path(...)` syntax in the update value, the expression will be treated as a compound Java bean properties path expression. This path @@ -203,7 +233,7 @@ Here, the `trip_short_name` field is updated for each trip in the feed. The value will be copied from the `route_long_name` field of each trip's associated route. -### Retain an Entity +#### Retain an Entity We also provide a powerful mechanism for selecting just a sub-set of a feed. You can apply retain operations to entities you wish to keep and all the supporting entities referenced @@ -228,7 +258,7 @@ example: {"op":"retain","match":{"file":"routes.txt", "route_short_name":"B15"}, "retainBlocks":false} ``` -### Remove an Entity +#### Remove an Entity You can remove a specific entity from a feed. @@ -239,7 +269,7 @@ You can remove a specific entity from a feed. Note that removing an entity has a cascading effect. If you remove a trip, all the stop times that depend on that trip will also be removed. If you remove a route, all the trips and stop times for that route will be removed. -### Trim a Trip +#### Trim a Trip You can remove stop times from the beginning or end of a trip using the "trim_trip" operation. Example: @@ -260,7 +290,7 @@ Or both: {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "to_stop_id":"125S", "from_stop_id":"138S"} ``` -### Generate Stop Times +#### Generate Stop Times You can generate stop time entries for a trip. Example: @@ -273,7 +303,7 @@ stops. The departure time for the first stop will be set from the `start_time` be set from the `end_time` field, and the times for intermediate stops will be interpolated based on their distance along the trip. -### Extend Service Calendars +#### Extend Service Calendars Sometimes you need to extend the service dates in a GTFS feed, perhaps in order to temporarily extend an expired feed. Extending the feed by hand can be a tedious task, especially when the feed uses a complex combination of `calendar.txt` and `calendar_dates.txt` @@ -309,7 +339,7 @@ _Note_: We don't make any effort to extend canceled service dates, as specified other special events. It's too tricky to automatically determine how they should be handled. You may need to still handle those manually. -### Deduplicate Calendar Entries +#### Deduplicate Calendar Entries Finds GTFS service_ids that have the exact same set of active days and consolidates each set of duplicated ids to a single service_id entry. @@ -318,7 +348,7 @@ ids to a single service_id entry. {"op":"deduplicate_service_ids"} ``` -### Merge Trips and Simplify Calendar Entries +#### Merge Trips and Simplify Calendar Entries Some agencies model their transit schedule favoring multiple entries in calendar_dates.txt as opposed to a more concise entry in calendar.txt. A smaller number of agencies take this scheme even further, creating trips.txt entries for each @@ -355,7 +385,7 @@ The transform takes additional optional arguments to control its behavior: where appropriate. -### Shift Negative Stop Times +#### Shift Negative Stop Times Some agencies have trips that they model as starting BEFORE midnight on a given service date. For these agencies, it would be convenient to represent these trips with negative arrival and departure times in stop_times.txt. The GTFS spec and @@ -403,13 +433,13 @@ _A note on negative stop times:_ When writing negative stop times, the negative {"op":"last_stop_to_headsign"} ``` -### Arbitrary Transform +#### Arbitrary Transform We also allow you to specify arbitrary transformations as well. Here, you specify your transformation class and we will automatically instantiate it for use in the transform pipeline. ``` -{"op":"transform", "class":"some.class.implementing.GtfsTranformStrategy"} +{"op":"transform", "class":"some.class.implementing.GtfsTransformStrategy"} ``` We additionally provide a mechanism for setting additional properties of the transform. For all additional properties From 961bfbd15d932930a49ebaa99662b1cf654d1e99 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:41:09 +0200 Subject: [PATCH 072/234] Update release task --- .github/workflows/release.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cf6ba0e6..14d32f61a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - run: | + release_name=echo "{{github.ref_name}}" | sed -e "s/onebusaway-gtfs-modules-//" + echo "RELEASE_NAME=$release_name" >> $GITHUB_ENV - name: Release uses: softprops/action-gh-release@v2 with: - generate_release_notes: true \ No newline at end of file + generate_release_notes: true + name: {{ env.RELEASE_NAME }} \ No newline at end of file From b8bd77e145f66f3c1017a2c6c45c56215ac31a9f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 11:46:40 +0200 Subject: [PATCH 073/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index d6f2d14d7..2970eb669 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 5e0eafc1e..ac9f127ac 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 64d9f3ad5..4eba3b0b1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index b009468ef..aa7ebb9c8 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 29bcf0b99..8f8a9c96a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 7a9781f98..161fc063b 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 519bc889e..70515eb75 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 315a26973..b73038b4f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 2.0.3-SNAPSHOT + 3.0.1-SNAPSHOT pom onebusaway-gtfs-modules From 42363a691ca798b40c74e2299089e8283ead81eb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 12:10:34 +0200 Subject: [PATCH 074/234] Fix release generation --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14d32f61a..12bd9b9cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,8 +17,8 @@ jobs: - run: | release_name=echo "{{github.ref_name}}" | sed -e "s/onebusaway-gtfs-modules-//" echo "RELEASE_NAME=$release_name" >> $GITHUB_ENV + echo $release_name - name: Release uses: softprops/action-gh-release@v2 with: - generate_release_notes: true - name: {{ env.RELEASE_NAME }} \ No newline at end of file + generate_release_notes: true \ No newline at end of file From f6670b67740bb554df295e2e7f8d71c0d806cac4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 12:14:34 +0200 Subject: [PATCH 075/234] [maven-release-plugin] prepare release onebusaway-gtfs-modules-3.0.0 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 2970eb669..ecf860f17 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index ac9f127ac..ba9f1bcd2 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 4eba3b0b1..047cc7b91 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.1-SNAPSHOT + 3.0.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index aa7ebb9c8..19f78a92b 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.1-SNAPSHOT + 3.0.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 8f8a9c96a..df3e866c0 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 161fc063b..098c595e6 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 70515eb75..8b6390219 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/pom.xml b/pom.xml index b73038b4f..556752e62 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.0.0 pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + onebusaway-gtfs-modules-3.0.0 From 020b5ecea3d0192feda45d5e2ce0d27b157ded3a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 12:14:36 +0200 Subject: [PATCH 076/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index ecf860f17..2970eb669 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index ba9f1bcd2..ac9f127ac 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 047cc7b91..4eba3b0b1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.0 + 3.0.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 19f78a92b..aa7ebb9c8 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.0 + 3.0.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index df3e866c0..8f8a9c96a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 098c595e6..161fc063b 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 8b6390219..70515eb75 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 556752e62..b73038b4f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.0 + 3.0.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - onebusaway-gtfs-modules-3.0.0 + HEAD From 9c4ca725059a0e35467287b77da9d70b8dfbc835 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 14:56:36 +0200 Subject: [PATCH 077/234] Remove .travis.yml --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7060531e5..000000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: java -jdk: - - openjdk8 -cache: - directories: - - $HOME/.m2 From a46e959682a4c9ddbe0d4638b17a2b0e07586edb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 15:03:19 +0200 Subject: [PATCH 078/234] Configure simpler tag for releases --- .github/workflows/release.yml | 4 ---- pom.xml | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12bd9b9cc..0cf6ba0e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - run: | - release_name=echo "{{github.ref_name}}" | sed -e "s/onebusaway-gtfs-modules-//" - echo "RELEASE_NAME=$release_name" >> $GITHUB_ENV - echo $release_name - name: Release uses: softprops/action-gh-release@v2 with: diff --git a/pom.xml b/pom.xml index b73038b4f..16e174c8c 100644 --- a/pom.xml +++ b/pom.xml @@ -218,6 +218,7 @@ 3.1.1 true + v${project.version} From e8239399845365f04f90afb7e3a7adf6820877b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:46:27 +0000 Subject: [PATCH 079/234] Add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..5db72dd6a --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} From bb7cefa21062c7b849ff83498057780b9a5fde0c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:51:06 +0000 Subject: [PATCH 080/234] Update dependency org.hsqldb:hsqldb to v2.7.1 [SECURITY] --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index ac9f127ac..76eb2edee 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -29,7 +29,7 @@ org.hsqldb hsqldb - 2.5.1 + 2.7.1 test From d5a400a8f2c88cfa504f0b1759539d694b42b627 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:51:10 +0000 Subject: [PATCH 081/234] Update dependency com.sun.xml.bind:jaxb-core to v2.3.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 16e174c8c..f5b5d1cf3 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ com.sun.xml.bind jaxb-core - 2.3.0 + 2.3.0.1 com.sun.xml.bind From d2d1e3c95385f50bddbb1d8c2a742109c2a16034 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 17:53:30 +0200 Subject: [PATCH 082/234] Update renovate config --- renovate.json | 6 ----- renovate.json5 | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) delete mode 100644 renovate.json create mode 100644 renovate.json5 diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 5db72dd6a..000000000 --- a/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/renovate.json5 b/renovate.json5 new file mode 100644 index 000000000..951de0bb2 --- /dev/null +++ b/renovate.json5 @@ -0,0 +1,59 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "prConcurrentLimit": 3, + "rebaseWhen": "conflicted", + "packageRules": [ + // some dependencies that we auto-merge release very often and even the auto-merges create a lot of + // noise, so we slow it down a bit + { + "description": "Automerge test dependencies in a single PR", + "groupName": "Test dependencies", + "matchPackageNames": [ + "org.mockito:mockito-core", + "com.tngtech.archunit:archunit", + "org.apache.maven.plugins:maven-surefire-plugin", + "me.fabriciorby:maven-surefire-junit5-tree-reporter", + "com.google.truth:truth", + "org.jacoco:jacoco-maven-plugin", // coverage plugin + "org.apache.commons:commons-compress" // only used by tests + ], + "matchPackagePrefixes": [ + "org.junit.jupiter:", + ], + "automerge": true, + "schedule": "on the 17th day of the month" + }, + { + "description": "Automerge Maven plugins in a single PR", + "groupName": "Maven plugins", + "matchPackageNames": [ + "io.github.git-commit-id:git-commit-id-maven-plugin", + "org.apache.maven.plugins:maven-gpg-plugin", + "org.codehaus.mojo:build-helper-maven-plugin", + "org.apache.maven.plugins:maven-source-plugin", + "com.hubspot.maven.plugins:prettier-maven-plugin", + "com.google.cloud.tools:jib-maven-plugin", + "org.apache.maven.plugins:maven-shade-plugin", + "org.apache.maven.plugins:maven-compiler-plugin", + "org.apache.maven.plugins:maven-jar-plugin", + "org.sonatype.plugins:nexus-staging-maven-plugin" + ], + "schedule": "on the 23rd day of the month", + "automerge": true + }, + { + "description": "Automerge logging dependencies in a single PR", + "groupName": "logging dependencies", + "matchPackagePrefixes": [ + "org.slf4j:", + "ch.qos.logback:" + ], + "automerge": true, + "schedule": "on the 4th day of the month" + } + ], + "timezone": "Europe/Berlin" +} From b80596060aa0e2b79e06bf853addf4d4e8f8a7e9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 17:59:07 +0200 Subject: [PATCH 083/234] Fix maven plugin config --- renovate.json5 | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index 951de0bb2..14f72cb21 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -29,17 +29,8 @@ { "description": "Automerge Maven plugins in a single PR", "groupName": "Maven plugins", - "matchPackageNames": [ - "io.github.git-commit-id:git-commit-id-maven-plugin", - "org.apache.maven.plugins:maven-gpg-plugin", - "org.codehaus.mojo:build-helper-maven-plugin", - "org.apache.maven.plugins:maven-source-plugin", - "com.hubspot.maven.plugins:prettier-maven-plugin", - "com.google.cloud.tools:jib-maven-plugin", - "org.apache.maven.plugins:maven-shade-plugin", - "org.apache.maven.plugins:maven-compiler-plugin", - "org.apache.maven.plugins:maven-jar-plugin", - "org.sonatype.plugins:nexus-staging-maven-plugin" + "matchPackagePrefixes": [ + "org.apache.maven.plugins:" ], "schedule": "on the 23rd day of the month", "automerge": true From 72623f10d8fbd649912439ad3bf1b4ebea50df60 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:01:50 +0000 Subject: [PATCH 084/234] Update dependency com.sun.xml.bind:jaxb-impl to v2.3.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f5b5d1cf3..b8384c691 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ com.sun.xml.bind jaxb-impl - 2.3.0 + 2.3.9 From 649dd7462f328d68333aa577c104ad9f6245e962 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:01:53 +0000 Subject: [PATCH 085/234] Update dependency javax.xml.bind:jaxb-api to v2.3.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f5b5d1cf3..93b1c6715 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ javax.xml.bind jaxb-api - 2.3.0 + 2.3.1 com.sun.xml.bind From 05a82616ce178e02dd48e5fc8c8b3232edaacf72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:01:58 +0000 Subject: [PATCH 086/234] Update logging dependencies to v2.0.16 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f5b5d1cf3..e7688d679 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ UTF-8 1.1.7 1.2.8 - 2.0.6 + 2.0.16 0.0.13 5.11.0 From 2d2a14d69626859e313f15ae3b931b0371d15b2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:02:02 +0000 Subject: [PATCH 087/234] Update Maven plugins --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index f5b5d1cf3..2f8f8ed72 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 17 17 @@ -171,7 +171,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 2.4 attach-sources @@ -184,7 +184,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.7.0 + 3.10.0 none false @@ -201,7 +201,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.5 sign-artifacts @@ -224,7 +224,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.3.1 + 3.5.0 me.fabriciorby From 1b024bac9566b471f3a774a8cf5ec7ddc4d494d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:02:06 +0000 Subject: [PATCH 088/234] Update dependency org.mockito:mockito-core to v5.13.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f5b5d1cf3..26e72f18c 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ org.mockito mockito-core - 5.12.0 + 5.13.0 test From 253fddf9699cb7865746d0a1beb4b665cbbce1d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:07:55 +0000 Subject: [PATCH 089/234] Update dependency org.apache.maven.plugins:maven-source-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 995bd727a..746eb6bd5 100644 --- a/pom.xml +++ b/pom.xml @@ -171,7 +171,7 @@ org.apache.maven.plugins maven-source-plugin - 2.4 + 3.3.1 attach-sources From 9cfabe2219b6e0df2d9c2da241afadb164fbef0a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 18:13:26 +0200 Subject: [PATCH 090/234] Finetune release --- Makefile | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index af44d2924..69e45b7ad 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ release: - mvn release:prepare release:perform -Dgoals=deploy release:clean + mvn release:clean release:prepare release:perform -Dgoals=deploy release:clean diff --git a/pom.xml b/pom.xml index 746eb6bd5..87aa6d440 100644 --- a/pom.xml +++ b/pom.xml @@ -218,7 +218,7 @@ 3.1.1 true - v${project.version} + v@{project.version} From bd7e50363fc7693fda1dc5e932e6c823953e589c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 18:14:53 +0200 Subject: [PATCH 091/234] [maven-release-plugin] prepare release v3.1.0 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 2970eb669..1c676554a 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 76eb2edee..29a1a1b5b 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 4eba3b0b1..71e2d0deb 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.1-SNAPSHOT + 3.1.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index aa7ebb9c8..a23994d10 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.0.1-SNAPSHOT + 3.1.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 8f8a9c96a..15028c457 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 161fc063b..a409d5b0c 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 70515eb75..3a64c6454 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 87aa6d440..b0ed3ec26 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.0.1-SNAPSHOT + 3.1.0 pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v3.1.0 From 5d7e7ca07b9e57b676b5738c6d5e8b377aec6055 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 18:14:55 +0200 Subject: [PATCH 092/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 1c676554a..0bd132fa3 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 29a1a1b5b..713811f98 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 71e2d0deb..1ef851ac8 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.1.0 + 3.1.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index a23994d10..6350c8c33 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.1.0 + 3.1.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 15028c457..047421028 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a409d5b0c..fa0414ad8 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 3a64c6454..3b829a67d 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index b0ed3ec26..b92d9e170 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.0 + 3.1.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -40,7 +40,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v3.1.0 + HEAD From e932b9eeb8f318cf1d96b285f70c81d3849cae87 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 18:19:51 +0200 Subject: [PATCH 093/234] Change tag --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cf6ba0e6..39281602d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Create Github release on: push: tags: - - "onebusaway-gtfs-modules-*.*.*" + - "v*.*.*" permissions: contents: write From 223bc441778a42258caf5476cbf62a411ba6aa31 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:25:04 +0000 Subject: [PATCH 094/234] Update dependency com.fasterxml.jackson.core:jackson-databind to v2.17.2 --- onebusaway-gtfs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 3b829a67d..376cf1fb3 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -31,7 +31,7 @@ com.fasterxml.jackson.core jackson-databind - 2.14.0 + 2.17.2 de.grundid.opendatalab From 98bf2e4e9b2763afdb49321186677550d1a6a1dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:25:10 +0000 Subject: [PATCH 095/234] Update dependency org.json:json to v20240303 --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index fa0414ad8..d17a21dc1 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -31,7 +31,7 @@ org.json json - 20231013 + 20240303 org.junit.jupiter From 208c9b95389aec0317e9db493812e7b46ed68a68 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:29:37 +0000 Subject: [PATCH 096/234] Update dependency org.hsqldb:hsqldb to v2.7.3 --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 713811f98..2550b2a67 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -29,7 +29,7 @@ org.hsqldb hsqldb - 2.7.1 + 2.7.3 test From e1c39c9b29c5c2843e7a068c1b005af2ed609645 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:29:40 +0000 Subject: [PATCH 097/234] Update dependency com.kurtraschke:wsf-api to v1.1 --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index d17a21dc1..5339980a9 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -51,7 +51,7 @@ com.kurtraschke wsf-api - 1.0 + 1.1 org.onebusaway From ae53d56693768d8a4092fcecb0603dc16fa79b15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:59:42 +0000 Subject: [PATCH 098/234] Update dependency com.mysql:mysql-connector-j to v8.4.0 --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 2550b2a67..f358b67bd 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -35,7 +35,7 @@ com.mysql mysql-connector-j - 8.2.0 + 8.4.0 org.slf4j From 5a2e50f69adbcdeed1f4f9ee05936499130521ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:59:46 +0000 Subject: [PATCH 099/234] Update dependency commons-cli:commons-cli to v1.9.0 --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 0bd132fa3..257817ed8 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -19,7 +19,7 @@ commons-cli commons-cli - 1.2 + 1.9.0 org.slf4j diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 1ef851ac8..003300ff2 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -25,7 +25,7 @@ commons-cli commons-cli - 1.2 + 1.9.0 org.slf4j diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 047421028..ccaacca65 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -27,7 +27,7 @@ commons-cli commons-cli - 1.2 + 1.9.0 org.slf4j From 0e020a315f39566d513ea8807a3755d304caa0e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:02:41 +0000 Subject: [PATCH 100/234] Update dependency org.hibernate:hibernate-core to v5.6.15.Final --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index f358b67bd..585152721 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -22,7 +22,7 @@ org.hibernate hibernate-core - 5.4.24.Final + 5.6.15.Final From f0dd7aa95c15aa48b9e10b4ab7ea8688407c3d75 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:02:44 +0000 Subject: [PATCH 101/234] Update dependency com.mysql:mysql-connector-j to v9 --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index f358b67bd..59f8e60e4 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -35,7 +35,7 @@ com.mysql mysql-connector-j - 8.4.0 + 9.0.0 org.slf4j From ea4cd58bce6fe20f0208e9f4b6533ba5c2e97a75 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:17:44 +0000 Subject: [PATCH 102/234] Update dependency com.sun.xml.bind:jaxb-core to v4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b92d9e170..383d6fec7 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ com.sun.xml.bind jaxb-core - 2.3.0.1 + 4.0.5 com.sun.xml.bind From d744d1060d8f345b8b486d784786ef493f0cb311 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:17:50 +0000 Subject: [PATCH 103/234] Update dependency com.sun.xml.bind:jaxb-impl to v4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b92d9e170..1c4f945e7 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ com.sun.xml.bind jaxb-impl - 2.3.9 + 4.0.5 From d8f0552d5a5cad90670b1944b7f1f49f7ea47973 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:31:05 +0200 Subject: [PATCH 104/234] Add onebusaway-collections module --- onebusaway-collections/pom.xml | 32 ++ .../src/main/java/META-INF/MANIFEST.MF | 3 + .../collections/CollectionsLibrary.java | 34 ++ .../ConcurrentCollectionsLibrary.java | 167 ++++++++++ .../org/onebusaway/collections/Counter.java | 122 ++++++++ .../onebusaway/collections/DirectedGraph.java | 164 ++++++++++ .../onebusaway/collections/FactoryMap.java | 291 ++++++++++++++++++ .../collections/FunctionalLibrary.java | 47 +++ .../collections/MappingLibrary.java | 193 ++++++++++++ .../java/org/onebusaway/collections/Max.java | 33 ++ .../java/org/onebusaway/collections/Min.java | 55 ++++ .../org/onebusaway/collections/Range.java | 78 +++++ .../adapter/AdaptableCollection.java | 43 +++ .../collections/adapter/AdaptableSet.java | 43 +++ .../adapter/AdaptableValueMapEntry.java | 48 +++ .../adapter/AdaptableValueSortedMap.java | 144 +++++++++ .../collections/adapter/AdapterLibrary.java | 83 +++++ .../collections/adapter/IAdapter.java | 20 ++ .../collections/adapter/IterableAdapter.java | 38 +++ .../collections/adapter/IteratorAdapter.java | 42 +++ .../collections/adapter/ListAdapter.java | 52 ++++ .../adapter/MapEntryValueAdapter.java | 61 ++++ .../beans/DefaultPropertyMethodResolver.java | 125 ++++++++ .../beans/PropertyInvocationResult.java | 28 ++ .../collections/beans/PropertyMethod.java | 46 +++ .../collections/beans/PropertyMethodImpl.java | 46 +++ .../beans/PropertyMethodResolver.java | 36 +++ .../PropertyPathCollectionExpression.java | 174 +++++++++++ .../beans/PropertyPathExpression.java | 165 ++++++++++ .../combinations/CombinationIterator.java | 85 +++++ .../combinations/Combinations.java | 129 ++++++++ .../combinations/PermutationIterator.java | 70 +++++ .../combinations/SequentialPairIterator.java | 63 ++++ .../onebusaway/collections/tuple/Pair.java | 59 ++++ .../collections/tuple/PairImpl.java | 97 ++++++ .../org/onebusaway/collections/tuple/T2.java | 40 +++ .../onebusaway/collections/tuple/T2Impl.java | 77 +++++ .../onebusaway/collections/tuple/Tuples.java | 67 ++++ .../src/site/apt/index.apt.vm | 31 ++ .../src/site/apt/release-notes.apt.vm | 60 ++++ onebusaway-collections/src/site/site.xml | 14 + .../collections/CollectionsLibraryTest.java | 39 +++ .../ConcurrentCollectionsLibraryTest.java | 85 +++++ .../collections/FactoryMapTest.java | 59 ++++ .../collections/FunctionalLibraryTest.java | 75 +++++ .../collections/MappingLibraryTest.java | 125 ++++++++ .../org/onebusaway/collections/MinTest.java | 67 ++++ .../adapter/AdaptableValueSortedMapTest.java | 83 +++++ .../PropertyPathCollectionExpressionTest.java | 99 ++++++ .../beans/PropertyPathExpressionTest.java | 156 ++++++++++ .../combinations/CombinationsTest.java | 79 +++++ .../collections/tuple/PairImplTest.java | 156 ++++++++++ .../collections/tuple/T2ImplTest.java | 69 +++++ pom.xml | 2 + 54 files changed, 4299 insertions(+) create mode 100644 onebusaway-collections/pom.xml create mode 100644 onebusaway-collections/src/main/java/META-INF/MANIFEST.MF create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/CollectionsLibrary.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/ConcurrentCollectionsLibrary.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/Counter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/DirectedGraph.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/FactoryMap.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/FunctionalLibrary.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/MappingLibrary.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/Max.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/Min.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/Range.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableCollection.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableSet.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueMapEntry.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueSortedMap.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdapterLibrary.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IAdapter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IterableAdapter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IteratorAdapter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/ListAdapter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/MapEntryValueAdapter.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/DefaultPropertyMethodResolver.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyInvocationResult.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethod.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodImpl.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodResolver.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathCollectionExpression.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathExpression.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/CombinationIterator.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/Combinations.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/PermutationIterator.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/SequentialPairIterator.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Pair.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/PairImpl.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2Impl.java create mode 100644 onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Tuples.java create mode 100644 onebusaway-collections/src/site/apt/index.apt.vm create mode 100644 onebusaway-collections/src/site/apt/release-notes.apt.vm create mode 100644 onebusaway-collections/src/site/site.xml create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/CollectionsLibraryTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/ConcurrentCollectionsLibraryTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/FactoryMapTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/FunctionalLibraryTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/MappingLibraryTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/MinTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/adapter/AdaptableValueSortedMapTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathCollectionExpressionTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathExpressionTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/combinations/CombinationsTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/PairImplTest.java create mode 100644 onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/T2ImplTest.java diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml new file mode 100644 index 000000000..909fe7e00 --- /dev/null +++ b/onebusaway-collections/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + onebusaway-collections + jar + + onebusaway-collections + A library with a number of convenient methods for working with collections + + + org.onebusaway + onebusaway-gtfs-modules + 3.1.1-SNAPSHOT + ../pom.xml + + + + + junit + junit + 4.13.2 + test + + + io.github.classgraph + classgraph + 4.8.105 + + + + diff --git a/onebusaway-collections/src/main/java/META-INF/MANIFEST.MF b/onebusaway-collections/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..5e9495128 --- /dev/null +++ b/onebusaway-collections/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/CollectionsLibrary.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/CollectionsLibrary.java new file mode 100644 index 000000000..868b587ff --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/CollectionsLibrary.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class CollectionsLibrary { + + public static Set set(T... values) { + Set set = new HashSet(); + for (T value : values) + set.add(value); + return set; + } + + public static final boolean isEmpty(Collection c) { + return c == null || c.isEmpty(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/ConcurrentCollectionsLibrary.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/ConcurrentCollectionsLibrary.java new file mode 100644 index 000000000..141dbd949 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/ConcurrentCollectionsLibrary.java @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +public class ConcurrentCollectionsLibrary { + + private static final ListFactory _listFactory = new ListFactory(); + + private static final SetFactory _setFactory = new SetFactory(); + + public static void addToMapValueList( + ConcurrentMap> map, KEY key, VALUE value) { + CollectionFactory> factory = listFactory(); + addToMapValueCollection(map, key, value, factory); + } + + public static void removeFromMapValueList( + ConcurrentMap> map, KEY key, VALUE value) { + CollectionFactory> factory = listFactory(); + removeFromMapValueCollection(map, key, value, factory); + } + + public static void addToMapValueSet( + ConcurrentMap> map, KEY key, VALUE value) { + CollectionFactory> factory = setFactory(); + addToMapValueCollection(map, key, value, factory); + } + + public static void removeFromMapValueSet( + ConcurrentMap> map, KEY key, VALUE value) { + CollectionFactory> factory = setFactory(); + removeFromMapValueCollection(map, key, value, factory); + } + + /**** + * + ****/ + + private static > void addToMapValueCollection( + ConcurrentMap map, KEY key, VALUE value, + CollectionFactory factory) { + + while (true) { + + C values = map.get(key); + + if (values == null) { + C newKeys = factory.create(value); + values = map.putIfAbsent(key, newKeys); + if (values == null) + return; + } + + C origCopy = factory.copy(values); + + if (origCopy.contains(value)) + return; + + C extendedCopy = factory.copy(origCopy); + extendedCopy.add(value); + + if (map.replace(key, origCopy, extendedCopy)) + return; + } + } + + private static > void removeFromMapValueCollection( + ConcurrentMap map, KEY key, VALUE value, + CollectionFactory factory) { + + while (true) { + + C values = map.get(key); + + if (values == null) + return; + + C origCopy = factory.copy(values); + + if (!origCopy.contains(value)) + return; + + C reducedCopy = factory.copy(origCopy); + reducedCopy.remove(value); + + if (reducedCopy.isEmpty()) { + if (map.remove(key, origCopy)) + return; + } else { + if (map.replace(key, origCopy, reducedCopy)) + return; + } + } + } + + @SuppressWarnings("unchecked") + private static CollectionFactory> listFactory() { + return _listFactory; + } + + @SuppressWarnings("unchecked") + private static CollectionFactory> setFactory() { + return _setFactory; + } + + private interface CollectionFactory> { + public C create(VALUE value); + + public C copy(C existingValues); + } + + @SuppressWarnings("rawtypes") + private static class ListFactory implements CollectionFactory { + + @SuppressWarnings("unchecked") + @Override + public Collection create(Object value) { + List values = new ArrayList(1); + values.add(value); + return values; + } + + @SuppressWarnings("unchecked") + @Override + public Collection copy(Collection existingValues) { + return new ArrayList(existingValues); + } + } + + @SuppressWarnings("rawtypes") + private static class SetFactory implements CollectionFactory { + + @SuppressWarnings("unchecked") + @Override + public Collection create(Object value) { + Set values = new HashSet(); + values.add(value); + return values; + } + + @SuppressWarnings("unchecked") + @Override + public Collection copy(Collection existingValues) { + return new HashSet(existingValues); + } + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/Counter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/Counter.java new file mode 100644 index 000000000..82d98bccb --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/Counter.java @@ -0,0 +1,122 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Counter implements Serializable { + + private static final long serialVersionUID = 1L; + + private Map _counts = new HashMap(); + + private int _total = 0; + + public int size() { + return _counts.size(); + } + + public void increment(T key, int offset) { + int count = getCount(key) + offset; + _counts.put(key, count); + _total += offset; + } + + public void increment(T key) { + increment(key, 1); + } + + public void decrement(T key) { + increment(key, -1); + } + + public int getCount(T key) { + Integer count = _counts.get(key); + if (count == null) + count = 0; + return count; + } + + public Set getKeys() { + return _counts.keySet(); + } + + public Set> getEntrySet() { + return _counts.entrySet(); + } + + public int getTotal() { + return _total; + } + + public T getMax() { + int maxCount = 0; + T maxValue = null; + for (Map.Entry entry : _counts.entrySet()) { + if (maxValue == null || maxCount < entry.getValue()) { + maxValue = entry.getKey(); + maxCount = entry.getValue(); + } + } + return maxValue; + } + + /** + * @return sorted from min to max + */ + public List getSortedKeys() { + List values = new ArrayList(_counts.keySet()); + Collections.sort(values, new Comparator() { + public int compare(T o1, T o2) { + int a = getCount(o1); + int b = getCount(o2); + if (a == b) + return 0; + return a < b ? -1 : 1; + } + }); + return values; + } + + /******************************************************************************************************************* + * {@link Object} Interface + ******************************************************************************************************************/ + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof Counter)) + return false; + Counter c = (Counter) obj; + return _counts.equals(c._counts); + } + + @Override + public int hashCode() { + return _counts.hashCode(); + } + + @Override + public String toString() { + return _counts.toString(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/DirectedGraph.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/DirectedGraph.java new file mode 100644 index 000000000..15cae7d19 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/DirectedGraph.java @@ -0,0 +1,164 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +public class DirectedGraph { + + private Map> _outboundEdges = new HashMap>(); + + private Map> _inboundEdges = new HashMap>(); + + public DirectedGraph() { + + } + + public DirectedGraph(DirectedGraph graph) { + for (T node : graph.getNodes()) + addNode(node); + for (Pair edge : graph.getEdges()) + addEdge(edge.getFirst(), edge.getSecond()); + } + + public Set getNodes() { + Set nodes = new HashSet(); + nodes.addAll(_outboundEdges.keySet()); + nodes.addAll(_inboundEdges.keySet()); + return nodes; + } + + public Set> getEdges() { + Set> edges = new HashSet>(); + for (T from : _outboundEdges.keySet()) { + for (T to : _outboundEdges.get(from)) + edges.add(Tuples.pair(from, to)); + } + return edges; + } + + public Set getInboundNodes(T node) { + return get(_inboundEdges, node, false); + } + + public Set getOutboundNodes(T node) { + return get(_outboundEdges, node, false); + } + + public boolean isConnected(T from, T to) { + + if (from.equals(to)) + return true; + + return isConnected(from, to, new HashSet()); + } + + public void addNode(T node) { + get(_outboundEdges, node, true); + get(_inboundEdges, node, true); + } + + public void addEdge(T from, T to) { + get(_outboundEdges, from, true).add(to); + get(_inboundEdges, to, true).add(from); + } + + public void removeEdge(T from, T to) { + get(_outboundEdges, from, false).remove(to); + get(_inboundEdges, to, false).remove(from); + } + + private void removeNode(T node) { + + for (T from : get(_inboundEdges, node, false)) + get(_outboundEdges, from, false).remove(node); + _inboundEdges.remove(node); + + for (T to : get(_outboundEdges, node, false)) + get(_inboundEdges, to, false).remove(node); + _outboundEdges.remove(node); + } + + public List getTopologicalSort(Comparator tieBreaker) { + + List order = new ArrayList(); + DirectedGraph g = new DirectedGraph(this); + + while (true) { + + Set nodes = g.getNodes(); + + if (nodes.isEmpty()) + return order; + + List noInbound = new ArrayList(); + + for (T node : nodes) { + if (g.getInboundNodes(node).isEmpty()) + noInbound.add(node); + } + + if (noInbound.isEmpty()) + throw new IllegalStateException("cycle"); + + if (tieBreaker != null) + Collections.sort(noInbound, tieBreaker); + + T node = noInbound.get(0); + order.add(node); + g.removeNode(node); + } + } + + /**** + * Private Methods + ****/ + + private boolean isConnected(T from, T to, Set visited) { + + if (from.equals(to)) + return true; + + for (T next : get(_outboundEdges, from, false)) { + if (visited.add(next)) { + if (isConnected(next, to, visited)) + return true; + } + } + + return false; + } + + private Set get(Map> edges, T key, boolean create) { + Set set = edges.get(key); + if (set == null) { + set = new HashSet(); + if (create) + edges.put(key, set); + } + return set; + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/FactoryMap.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/FactoryMap.java new file mode 100644 index 000000000..5581388f1 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/FactoryMap.java @@ -0,0 +1,291 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +/** + * A extension of {@link HashMap} that will automatically create a {@link Map} + * key-value entry if a call to {@link #get(Object)} is made where the key is + * not already present in the map. + * + * Default map entries can be created by passing in an instance of + * {@link IValueFactory} as an object factory (see + * {@link #FactoryMap(IValueFactory)}). + * + * Maps can also be created by passing a plain-old Java object. The class of the + * Java object will be used to create new value instances on demand, as long the + * class has a no-arg constructor (see {@link #FactoryMap(Object)}). + * + * @author bdferris + */ +public class FactoryMap extends HashMap { + + private static final long serialVersionUID = 1L; + + private IValueFactory _valueFactory; + + /** + * Object factory interface for creating a new value for a specified key for + * use in {@link FactoryMap} + * + * @author bdferris + */ + public interface IValueFactory { + public VF create(KF key); + } + + /** + * A convenience method for creating an instance of {@link FactoryMap} that + * wraps an existing {@link Map} and has a specific default value. The default + * value's class will be used to create new value instances as long as it has + * a no-arg constructor. + * + * @param map an existing map to wrap + * @param defaultValue see {@link #FactoryMap(Object)} for discussion + * @return a {@link Map} with factory-map behavior + */ + public static Map create(Map map, V defaultValue) { + return new MapImpl(map, new ClassInstanceFactory( + defaultValue.getClass())); + } + + /** + * A convenience method for creating an instance of {@link FactoryMap} that + * wraps an existing {@link Map} and has a specific default value factory. + * + * @param map an existing map to wrap + * @param factory see {@link #FactoryMap(IValueFactory)} for discussion + * @return a {@link Map} with factory-map behavior + */ + public static Map create(Map map, + IValueFactory factory) { + return new MapImpl(map, factory); + } + + /** + * A convenience method for creating an instance of {@link FactoryMap} that + * wraps an existing {@link SortedMap} and has a specific default value. The + * default value's class will be used to create new value instances as long as + * it has a no-arg constructor. + * + * @param map an existing sorted map to wrap + * @param defaultValue see {@link #FactoryMap(Object)} for discussion + * @return a {@link SortedMap} with factory-map behavior + */ + public static SortedMap createSorted(SortedMap map, + V defaultValue) { + return new SortedMapImpl(map, new ClassInstanceFactory( + defaultValue.getClass())); + } + + /** + * A convenience method for creating an instance of {@link FactoryMap} that + * wraps an existing {@link SortedMap} and has a specific default value + * factory. + * + * @param map an existing sorted map to wrap + * @param factory see {@link #FactoryMap(IValueFactory)} for discussion + * @return a {@link SortedMap} with factory-map behavior + */ + public static SortedMap createSorted(SortedMap map, + IValueFactory factory) { + return new SortedMapImpl(map, factory); + } + + /** + * A factory map constructor that accepts a default value instance. The + * {@link Class} of the default value instance will be used to create new + * default value instances as needed assuming the class has no-arg + * constructor. New values will be created when calls are made to + * {@link #get(Object)} and the specified key is not already present in the + * map. Why do we accept an object instance instead of a class instance? It + * makes it easier to handle cases where V is itself a parameterized type. + * + * @param factoryInstance the {@link Class} of the instance will be used to + * create new values as needed + */ + public FactoryMap(V factoryInstance) { + this(new ClassInstanceFactory(factoryInstance.getClass())); + } + + /** + * A factory map constructor that accepts a {@link IValueFactory} default + * value factory. The value factory will be called when calls are made to + * {@link #get(Object)} and the specified key is not already present in the + * map. + * + * @param valueFactory the default value factory + */ + public FactoryMap(IValueFactory valueFactory) { + _valueFactory = valueFactory; + } + + /** + * Returns the value to which the specified key is mapped, or a default value + * instance if the specified key is not present in the map. Subsequent clals + * to {@link #get(Object)} with the same key will return the same value + * instance. + * + * @see Map#get(Object) + * @see #put(Object, Object) + */ + @SuppressWarnings("unchecked") + @Override + public V get(Object key) { + if (!containsKey(key)) + put((K) key, createValue((K) key)); + return super.get(key); + } + + private V createValue(K key) { + return _valueFactory.create(key); + } + + private static class ClassInstanceFactory implements + IValueFactory, Serializable { + + private static final long serialVersionUID = 1L; + + private Class _valueClass; + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ClassInstanceFactory(Class valueClass) { + _valueClass = valueClass; + } + + public V create(K key) { + try { + return _valueClass.newInstance(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } + + private static class MapImpl implements Map, Serializable { + + private static final long serialVersionUID = 1L; + + private Map _source; + + private IValueFactory _valueFactory; + + public MapImpl(Map source, IValueFactory valueFactory) { + _source = source; + _valueFactory = valueFactory; + } + + public void clear() { + _source.clear(); + } + + public boolean containsKey(Object key) { + return _source.containsKey(key); + } + + public boolean containsValue(Object value) { + return _source.containsValue(value); + } + + public Set> entrySet() { + return _source.entrySet(); + } + + @SuppressWarnings("unchecked") + public V get(Object key) { + if (!containsKey(key)) + _source.put((K) key, createValue((K) key)); + return _source.get(key); + } + + public boolean isEmpty() { + return _source.isEmpty(); + } + + public Set keySet() { + return _source.keySet(); + } + + public V put(K key, V value) { + return _source.put(key, value); + } + + public void putAll(Map t) { + _source.putAll(t); + } + + public V remove(Object key) { + return _source.remove(key); + } + + public int size() { + return _source.size(); + } + + public Collection values() { + return _source.values(); + } + + private V createValue(K key) { + return _valueFactory.create(key); + } + } + + private static class SortedMapImpl extends MapImpl implements + SortedMap { + + private static final long serialVersionUID = 1L; + + private SortedMap _source; + + public SortedMapImpl(SortedMap source, + IValueFactory valueFactory) { + super(source, valueFactory); + _source = source; + } + + public Comparator comparator() { + return _source.comparator(); + } + + public K firstKey() { + return _source.firstKey(); + } + + public SortedMap headMap(K toKey) { + return _source.headMap(toKey); + } + + public K lastKey() { + return _source.lastKey(); + } + + public SortedMap subMap(K fromKey, K toKey) { + return _source.subMap(fromKey, toKey); + } + + public SortedMap tailMap(K fromKey) { + return _source.tailMap(fromKey); + } + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/FunctionalLibrary.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/FunctionalLibrary.java new file mode 100644 index 000000000..82a847753 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/FunctionalLibrary.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.util.ArrayList; +import java.util.List; + +import org.onebusaway.collections.beans.PropertyPathExpression; + +public final class FunctionalLibrary { + private FunctionalLibrary() { + + } + + public static List filter(Iterable elements, + String propertyPathExpression, Object value) { + List matches = new ArrayList(); + PropertyPathExpression query = new PropertyPathExpression( + propertyPathExpression); + for (T element : elements) { + Object result = query.invoke(element); + if ((value == null && result == null) + || (value != null && value.equals(result))) + matches.add(element); + } + return matches; + } + + public static T filterFirst(Iterable elements, + String propertyPathExpression, Object value) { + List matches = filter(elements, propertyPathExpression, value); + return matches.isEmpty() ? null : matches.get(0); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/MappingLibrary.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/MappingLibrary.java new file mode 100644 index 000000000..746d471ba --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/MappingLibrary.java @@ -0,0 +1,193 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +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.Set; + +import org.onebusaway.collections.beans.PropertyPathExpression; + +/** + * A number of functional-programming-inspired convenience methods for mapping + * one set of values to another. + * + * @author bdferris + * @see PropertyPathExpression + */ +public class MappingLibrary { + + /** + * Iterate over a collection of values, evaluating a + * {@link PropertyPathExpression} on each value, and constructing a + * {@link List} from the expression results. + * + * @param values an iterable collection of values to iterate over + * @param propertyPathExpression a property path expression to evaluate + * against each collection value + * @return a List composed of the property path expression evaluation results + */ + @SuppressWarnings("unchecked") + public static List map(Iterable values, + String propertyPathExpression) { + List mappedValues = new ArrayList(); + PropertyPathExpression query = new PropertyPathExpression( + propertyPathExpression); + for (T1 value : values) + mappedValues.add((T2) query.invoke(value)); + return mappedValues; + } + + /** + * This method is kept for backwards compatibility, and a more concise version + * can be found in {@link #map(Iterable, String)} + */ + public static List map(Iterable values, + String propertyPathExpression, Class resultType) { + return map(values, propertyPathExpression); + } + + /** + * Construct a {@link Map} from a set of values where the key for each value + * is the result from the evaluation of a {@link PropertyPathExpression} on + * each value. If two values in the iterable collection have the same key, + * subsequent values will overwrite previous values. + * + * @param values an iterable collection of values to iterate over + * @param propertyPathExpression a property path expression to evaluate + * against each collection value + * @return a map with values from the specified collection and keys from the + * property path expression + */ + @SuppressWarnings("unchecked") + public static Map mapToValue(Iterable values, + String propertyPathExpression) { + + Map byKey = new HashMap(); + PropertyPathExpression query = new PropertyPathExpression( + propertyPathExpression); + + for (V value : values) { + K key = (K) query.invoke(value); + byKey.put(key, value); + } + + return byKey; + } + + /** + * This method is kept for backwards compatibility, and a more concise version + * can be found in {@link #mapToValue(Iterable, String)} + */ + public static Map mapToValue(Iterable values, + String property, Class keyType) { + return mapToValue(values, property); + } + + /** + * Construct a {@link Map} from a set of values where the key for each value + * is the result of the evaluation of a {@link PropertyPathExpression} on each + * value. Each key maps to a {@link List} of values that all mapped to that + * same key. + * + * @param values an iterable collection of values to iterate over + * @param propertyPathExpression a property path expression to evaluate + * against each collection value + * @return a map with values from the specified collection and keys from the + * property path expression + */ + @SuppressWarnings("unchecked") + public static Map> mapToValueList(Iterable values, + String property) { + return mapToValueCollection(values, property, new ArrayList().getClass()); + } + + /** + * This method is kept for backwards compatibility, and a more concise version + * can be found in {@link #mapToValueList(Iterable, String)} + */ + @SuppressWarnings("unchecked") + public static Map> mapToValueList(Iterable values, + String property, Class keyType) { + return mapToValueCollection(values, property, new ArrayList().getClass()); + } + + /** + * Construct a {@link Map} from a set of values where the key for each value + * is the result of the evaluation of a {@link PropertyPathExpression} on each + * value. Each key maps to a {@link Set} of values that all mapped to that + * same key. + * + * @param values an iterable collection of values to iterate over + * @param propertyPathExpression a property path expression to evaluate + * against each collection value + * @return a map with values from the specified collection and keys from the + * property path expression + */ + + @SuppressWarnings("unchecked") + public static Map> mapToValueSet(Iterable values, + String property) { + return mapToValueCollection(values, property, new HashSet().getClass()); + } + + /** + * Construct a {@link Map} from a set of values where the key for each value + * is the result of the evaluation of a {@link PropertyPathExpression} on each + * value. Each key maps to a collection of values that all mapped to that same + * key. The collection type must have a no-arg constructor that can be used to + * create new collection instances as necessary. + * + * @param values an iterable collection of values to iterate over + * @param propertyPathExpression a property path expression to evaluate + * against each collection value + * @param collectionType the collection type used to contain mutiple values + * that map to the same key + * @return a map with values from the specified collection and keys from the + * property path expression + */ + @SuppressWarnings("unchecked") + public static , CIMPL extends C> Map mapToValueCollection( + Iterable values, String property, Class collectionType) { + + Map byKey = new HashMap(); + PropertyPathExpression query = new PropertyPathExpression(property); + + for (V value : values) { + + K key = (K) query.invoke(value); + C valuesForKey = byKey.get(key); + if (valuesForKey == null) { + + try { + valuesForKey = collectionType.newInstance(); + } catch (Exception ex) { + throw new IllegalStateException( + "error instantiating collection type: " + collectionType, ex); + } + + byKey.put(key, valuesForKey); + } + valuesForKey.add(value); + } + + return byKey; + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/Max.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/Max.java new file mode 100644 index 000000000..22443582c --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/Max.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +public class Max { + + private Min _min = new Min(); + + public void add(double value, T element) { + _min.add(-value, element); + } + + public T getMaxElement() { + return _min.getMinElement(); + } + + public double getMaxValue() { + return -_min.getMinValue(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/Min.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/Min.java new file mode 100644 index 000000000..88d41369a --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/Min.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import java.util.ArrayList; +import java.util.List; + +public class Min { + + private double _minValue = Double.POSITIVE_INFINITY; + + private List _minElements = new ArrayList(); + + public void add(double value, T element) { + if (value == _minValue) { + _minElements.add(element); + } else if (value < _minValue) { + _minElements.clear(); + _minElements.add(element); + _minValue = value; + } + } + + public boolean isEmpty() { + return _minElements.isEmpty(); + } + + public double getMinValue() { + return _minValue; + } + + public T getMinElement() { + if (_minElements.isEmpty()) + return null; + return _minElements.get(0); + } + + public List getMinElements() { + return _minElements; + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/Range.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/Range.java new file mode 100644 index 000000000..f53dc3925 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/Range.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +/** + * A simple, mutable double-precision range class for tracking a min and max + * value. + * + * @author bdferris + */ +public class Range { + + private double _min = Double.POSITIVE_INFINITY; + + private double _max = Double.NEGATIVE_INFINITY; + + public Range() { + + } + + public Range(double v) { + addValue(v); + } + + public Range(double from, double to) { + addValue(from); + addValue(to); + } + + public void addValue(double value) { + _min = Math.min(_min, value); + _max = Math.max(_max, value); + } + + public void setMin(double value) { + _min = value; + _max = Math.max(_max, value); + } + + public void setMax(double value) { + _min = Math.min(_min, value); + _max = value; + } + + public double getMin() { + return _min; + } + + public double getMax() { + return _max; + } + + public double getRange() { + return _max - _min; + } + + public boolean isEmpty() { + return _min > _max; + } + + @Override + public String toString() { + return _min + " " + _max; + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableCollection.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableCollection.java new file mode 100644 index 000000000..2fd9c9c7a --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableCollection.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; + +class AdaptableCollection extends AbstractCollection { + + private final Collection _source; + + private final IAdapter _adapater; + + public AdaptableCollection(Collection source, + IAdapter adapater) { + _source = source; + _adapater = adapater; + } + + @Override + public Iterator iterator() { + return AdapterLibrary.adaptIterator(_source.iterator(), _adapater); + } + + @Override + public int size() { + return _source.size(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableSet.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableSet.java new file mode 100644 index 000000000..0c0344647 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableSet.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Set; + +class AdaptableSet extends AbstractSet { + + private final Set _source; + + private final IAdapter _adapter; + + public AdaptableSet(Set source, IAdapter adapter) { + _source = source; + _adapter = adapter; + } + + @Override + public Iterator iterator() { + return AdapterLibrary.adaptIterator(_source.iterator(), _adapter); + } + + @Override + public int size() { + return _source.size(); + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueMapEntry.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueMapEntry.java new file mode 100644 index 000000000..737f71efa --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueMapEntry.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.Map; +import java.util.Map.Entry; + +class AdaptableValueMapEntry implements + Map.Entry { + + private final Entry _source; + + private final IAdapter _adapter; + + public AdaptableValueMapEntry(Map.Entry source, + IAdapter adapter) { + _source = source; + _adapter = adapter; + } + + @Override + public KEY getKey() { + return _source.getKey(); + } + + @Override + public TO_VALUE getValue() { + return AdapterLibrary.apply(_adapter, _source.getValue()); + } + + @Override + public TO_VALUE setValue(TO_VALUE value) { + throw new UnsupportedOperationException(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueSortedMap.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueSortedMap.java new file mode 100644 index 000000000..b9634cd29 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdaptableValueSortedMap.java @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +class AdaptableValueSortedMap implements + SortedMap { + + private final SortedMap _source; + + private final IAdapter _adapter; + + public AdaptableValueSortedMap(SortedMap source, + IAdapter adapter) { + _source = source; + _adapter = adapter; + } + + @Override + public int size() { + return _source.size(); + } + + @Override + public boolean isEmpty() { + return _source.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return _source.containsKey(key); + } + + @Override + public TO_VALUE get(Object key) { + return adapt(_source.get(key)); + } + + @Override + public TO_VALUE remove(Object key) { + return adapt(_source.remove(key)); + } + + @Override + public void clear() { + _source.clear(); + } + + @Override + public Comparator comparator() { + return _source.comparator(); + } + + @Override + public SortedMap subMap(KEY fromKey, KEY toKey) { + return new AdaptableValueSortedMap( + _source.subMap(fromKey, toKey), _adapter); + } + + @Override + public SortedMap headMap(KEY toKey) { + return new AdaptableValueSortedMap( + _source.headMap(toKey), _adapter); + } + + @Override + public SortedMap tailMap(KEY fromKey) { + return new AdaptableValueSortedMap( + _source.tailMap(fromKey), _adapter); + } + + @Override + public KEY firstKey() { + return _source.firstKey(); + } + + @Override + public KEY lastKey() { + return _source.lastKey(); + } + + @Override + public Set keySet() { + return _source.keySet(); + } + + @Override + public Collection values() { + return AdapterLibrary.adaptCollection(_source.values(), _adapter); + } + + @Override + public Set> entrySet() { + return AdapterLibrary.adaptSet(_source.entrySet(), + new MapEntryValueAdapter(_adapter)); + } + + /**** + * Any value methods that include modification are unsupported + ****/ + + @Override + public TO_VALUE put(KEY key, TO_VALUE value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + /**** + * Private Methods + ****/ + + private TO_VALUE adapt(FROM_VALUE value) { + if (value == null) + return null; + return _adapter.adapt(value); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdapterLibrary.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdapterLibrary.java new file mode 100644 index 000000000..ee2e18e95 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/AdapterLibrary.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +public class AdapterLibrary { + + public static final TO apply(IAdapter adapter, FROM value) { + if (value == null) + return null; + return adapter.adapt(value); + } + + public static IAdapter getIdentityAdapter(Class type) { + return new IdentityAdapter(); + } + + public static Iterable adapt(Iterable source, + IAdapter adapter) { + return new IterableAdapter(source, adapter); + } + + public static Iterator adaptIterator(Iterator source, + IAdapter adapter) { + return new IteratorAdapter(source, adapter); + } + + public static Collection adaptCollection( + Collection source, IAdapter adapter) { + return new AdaptableCollection(source, adapter); + } + + public static Set adaptSet(Set source, + IAdapter adapter) { + return new AdaptableSet(source, adapter); + } + + public static Map.Entry adaptMapEntry( + Map.Entry source, IAdapter adapter) { + return new AdaptableValueMapEntry(source, + adapter); + } + + public static SortedMap adaptSortedMap( + SortedMap source, IAdapter adapter) { + return new AdaptableValueSortedMap(source, + adapter); + } + + /***************************************************************************** + * + ****************************************************************************/ + + private static final class IdentityAdapter implements IAdapter, + Serializable { + + private static final long serialVersionUID = 1L; + + public T adapt(T source) { + return source; + } + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IAdapter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IAdapter.java new file mode 100644 index 000000000..265040b36 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IAdapter.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +public interface IAdapter { + public TO adapt(FROM source); +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IterableAdapter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IterableAdapter.java new file mode 100644 index 000000000..fef484ebe --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IterableAdapter.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.io.Serializable; +import java.util.Iterator; + +public class IterableAdapter implements Iterable, Serializable { + + private static final long serialVersionUID = 1L; + + private Iterable _source; + + private IAdapter _adapter; + + public IterableAdapter(Iterable source, IAdapter adapter) { + _source = source; + _adapter = adapter; + } + + public Iterator iterator() { + return new IteratorAdapter(_source.iterator(),_adapter); + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IteratorAdapter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IteratorAdapter.java new file mode 100644 index 000000000..42eeb1664 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/IteratorAdapter.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.Iterator; + +class IteratorAdapter implements Iterator { + + private final Iterator _it; + + private final IAdapter _adapter; + + public IteratorAdapter(Iterator it, IAdapter adapter) { + _it = it; + _adapter = adapter; + } + + public boolean hasNext() { + return _it.hasNext(); + } + + public TO next() { + return _adapter.adapt(_it.next()); + } + + public void remove() { + _it.remove(); + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/ListAdapter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/ListAdapter.java new file mode 100644 index 000000000..70297e46b --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/ListAdapter.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.AbstractList; +import java.util.List; + +/** + * Create an adapted {@link List} instance that adapts a list of type FROM to + * type TO using a {@link IAdapter} instance. The adapted list will be immutable + * but will reflect changes to the underlying list. + * + * @author bdferris + * + * @param + * @param + */ +public class ListAdapter extends AbstractList { + + private final List _source; + + private final IAdapter _adapter; + + public ListAdapter(List source, IAdapter adapter) { + _source = source; + _adapter = adapter; + } + + @Override + public TO get(int index) { + FROM v = _source.get(index); + return _adapter.adapt(v); + } + + @Override + public int size() { + return _source.size(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/MapEntryValueAdapter.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/MapEntryValueAdapter.java new file mode 100644 index 000000000..31a2a6bf2 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/adapter/MapEntryValueAdapter.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import java.util.Map.Entry; + +class MapEntryValueAdapter implements + IAdapter, Entry> { + + private IAdapter _adapter; + + public MapEntryValueAdapter(IAdapter adapter) { + _adapter = adapter; + } + + @Override + public Entry adapt(Entry source) { + TO_VALUE v = AdapterLibrary.apply(_adapter, source.getValue()); + return new EntryImpl(source.getKey(), v); + } + + private static class EntryImpl implements Entry { + + private final K _key; + private final V _value; + + public EntryImpl(K key, V value) { + _key = key; + _value = value; + } + + @Override + public K getKey() { + return _key; + } + + @Override + public V getValue() { + return _value; + } + + @Override + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/DefaultPropertyMethodResolver.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/DefaultPropertyMethodResolver.java new file mode 100644 index 000000000..22608f33a --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/DefaultPropertyMethodResolver.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +public class DefaultPropertyMethodResolver implements PropertyMethodResolver { + + private static final String OBA_IFACE_PATH = "org.onebusaway.transit_data_federation.services.transit_graph."; + private static final String OBA_IMPL_PATH = "org.onebusaway.transit_data_federation.impl.transit_graph."; + private static Map> interfaceMethodsByKey = new HashMap(); + private static Map interfaceToImplMap; + static { + // TODO inject these somehow + interfaceToImplMap = new HashMap<>(); + // support GTFS interface corner case + interfaceToImplMap.put("org.onebusaway.gtfs.model.StopLocation", "org.onebusaway.gtfs.model.Stop"); + // support OBA interfaces + String[] entryInterfaces = {"AgencyEntry", "BlockConfigurationEntry", "BlockEntry", "BlockStopTimeEntry", + "BlockTripEntry", "FrequencyBlockStopTimeEntry", "FrequencyEntry", "RouteCollectionEntry", + "RouteEntry", "StopEntry", "StopTimeEntry", "TripEntry"}; + for (String interfaceName : entryInterfaces) { + interfaceToImplMap.put(OBA_IFACE_PATH + interfaceName, + OBA_IMPL_PATH + interfaceName + "Impl"); + } + } + + @Override + public PropertyMethod getPropertyMethod(Class targetType, + String propertyName) { + String methodName = "get"; + for(String part : propertyName.split(" |_")) { + methodName += part.substring(0, 1).toUpperCase() + + part.substring(1); + } + Method method = null; + try { + if(targetType.isInterface()) { + List methods = getCachedInterfaceMethods(targetType, propertyName, methodName); + if(methods.size() == 1) + method = methods.get(0); + else { + throw new IllegalStateException("Ambiguous implementation set for interface: " + + targetType + " /" + + methodName + + " with potentials: " + methods + + " and " + interfaceToImplMap.keySet() + " known interface mappings"); + } + } else + method = targetType.getMethod(methodName); + } catch (Exception ex) { + throw new IllegalStateException("error introspecting class: " + + targetType, ex); + } + if (method == null) { + throw new IllegalStateException("could not find property \"" + + propertyName + "\" for type " + targetType.getName()); + } + method.setAccessible(true); + return new PropertyMethodImpl(method); + } + + + private List getCachedInterfaceMethods(Class targetType, String propertyName, String methodName) { + String key = hash(targetType, propertyName); + if (interfaceMethodsByKey.containsKey(key)) { + return interfaceMethodsByKey.get(key); + } + + ScanResult scanResult = new ClassGraph() + .acceptPackages("org.onebusaway") + .enableClassInfo() + .scan(); + + List methods = new ArrayList(); + for (ClassInfo ci : scanResult.getClassesImplementing(targetType.getCanonicalName())) { + try { + if (matches(ci.getName(), targetType)) { + methods.add(Class.forName(ci.getName()).getMethod(methodName)); + } + } catch(Exception e) { + continue; + } + } + interfaceMethodsByKey.put(key, methods); + + return methods; + } + + + private boolean matches(String reflectedTypeName, Class targetType) { + String targetTypeName = targetType.getName(); + if (interfaceToImplMap.containsKey(targetTypeName)) { + String implName = interfaceToImplMap.get(targetTypeName); + return implName.equals(reflectedTypeName); + } + return targetTypeName.equals(reflectedTypeName); + } + + private String hash(Class targetType, String propertyName) { + return targetType.getName() + "." + propertyName; + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyInvocationResult.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyInvocationResult.java new file mode 100644 index 000000000..dee30b396 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyInvocationResult.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +public class PropertyInvocationResult { + public final Object parent; + public final String propertyName; + public final Object value; + + public PropertyInvocationResult(Object parent, String propertyName, Object value) { + this.parent = parent; + this.propertyName = propertyName; + this.value = value; + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethod.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethod.java new file mode 100644 index 000000000..bb76776a5 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethod.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A wrapper interface that provides methods similar to + * {@link Method#invoke(Object, Object...)} for Java bean property getter + * methods (aka takes no arguments), but allows us to more easily swap in + * different underlying method implementations. + * + * @author bdferris + * @see PropertyMethodResolver + */ +public interface PropertyMethod { + + /** + * Invoke the property method on the specified target object and return the + * resulting value. + * + * @param target the target bean to invoke the property method on. + * @return the value resulting from the method invocation. + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public Object invoke(Object target) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException; + + public Class getReturnType(); +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodImpl.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodImpl.java new file mode 100644 index 000000000..0814d9db1 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodImpl.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * An implementation of {@link PropertyMethod} that uses a {@link Method} for + * the underlying invocation. + * + * @author bdferris + * @see PropertyMethod + */ +class PropertyMethodImpl implements PropertyMethod { + + private final Method _method; + + public PropertyMethodImpl(Method method) { + _method = method; + } + + @Override + public Object invoke(Object value) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + return _method.invoke(value); + } + + @Override + public Class getReturnType() { + return _method.getReturnType(); + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodResolver.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodResolver.java new file mode 100644 index 000000000..63ccbf683 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyMethodResolver.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +/** + * A strategy interface for resolving {@link PropertyMethod} instances for a + * target class and property name. + * + * @author bdferris + * @see PropertyMethod + */ +public interface PropertyMethodResolver { + + /** + * Resolve a property method for the target type and property name. + * + * @param targetType + * @param propertyName + * @return a property method implementation, or null if not found. + */ + public PropertyMethod getPropertyMethod(Class targetType, + String propertyName); +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathCollectionExpression.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathCollectionExpression.java new file mode 100644 index 000000000..5804c9622 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathCollectionExpression.java @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Simple support for Java bean property path expression parsing and evaluation, + * where the resulting evaluation produces multiple values. + * + * Example: + * + * interface Container { public List getEntries(); } + * + * interface Entry { public String getName(); } + * + * If you call {@link #evaluate(Object, String)} with a Container object and + * property path of "entries.name", the expression will iterate over each Entry + * in the Container, collecting the name property of each entry in the results. + * + * @author bdferris + */ +public final class PropertyPathCollectionExpression { + + private String[] _properties; + + private PropertyMethod[] _methods = null; + + private PropertyMethodResolver _resolver = new DefaultPropertyMethodResolver(); + + public static void evaluate(Object target, String query, + Collection values) { + PropertyPathCollectionExpression expression = new PropertyPathCollectionExpression( + query); + expression.invoke(target, values); + } + + public static List evaluate(Object target, String query) { + PropertyPathCollectionExpression expression = new PropertyPathCollectionExpression( + query); + List values = new ArrayList(); + expression.invoke(target, values); + return values; + } + + /** + * + * @param query the property path expression to evaluate + */ + public PropertyPathCollectionExpression(String query) { + _properties = query.split("\\."); + _methods = new PropertyMethod[_properties.length]; + } + + public void setPropertyMethodResolver(PropertyMethodResolver resolver) { + _resolver = resolver; + } + + public String getPath() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < _properties.length; i++) { + if (i > 0) + b.append('.'); + b.append(_properties[i]); + } + return b.toString(); + } + + /** + * Invoke the property path expression against the specified object value + * + * @param value the target bean to start the property path expression against. + * @param results the output collection where evaluated results will be + * stored. + */ + public void invoke(Object value, Collection results) { + invoke(null, value, 0, new ValueResultCollector(results)); + } + + public void invokeReturningFullResult(Object value, + Collection results) { + invoke(null, value, 0, new FullResultCollector(results)); + } + + private void invoke(Object parent, Object value, int methodIndex, + ResultCollector collector) { + if (methodIndex == _methods.length) { + String propertyName = _properties.length == 0 ? null + : _properties[_properties.length - 1]; + collector.addResult(parent, propertyName, value); + return; + } + if (value == null) { + return; + } + PropertyMethod m = getPropertyMethod(value.getClass(), methodIndex); + Object result = null; + try { + result = m.invoke(value); + } catch (Exception ex) { + throw new IllegalStateException("error invoking property reader: obj=" + + value + " property=" + _properties[methodIndex], ex); + } + if (result instanceof Iterable) { + Iterable iterable = (Iterable) result; + for (Object child : iterable) { + invoke(value, child, methodIndex + 1, collector); + } + } else if (result instanceof Object[]) { + Object[] values = (Object[]) result; + for (Object child : values) { + invoke(value, child, methodIndex + 1, collector); + } + } else { + invoke(value, result, methodIndex + 1, collector); + } + } + + private PropertyMethod getPropertyMethod(Class valueType, int methodIndex) { + PropertyMethod method = _methods[methodIndex]; + if (method == null) { + method = _resolver.getPropertyMethod(valueType, _properties[methodIndex]); + _methods[methodIndex] = method; + } + return method; + } + + private interface ResultCollector { + public void addResult(Object parent, String propertyName, Object value); + } + + private static class ValueResultCollector implements ResultCollector { + + private final Collection values; + + public ValueResultCollector(Collection values) { + this.values = values; + } + + @Override + public void addResult(Object parent, String propertyName, Object value) { + values.add(value); + } + } + + private static class FullResultCollector implements ResultCollector { + + private final Collection results; + + public FullResultCollector(Collection results) { + this.results = results; + } + + @Override + public void addResult(Object parent, String propertyName, Object value) { + results.add(new PropertyInvocationResult(parent, propertyName, value)); + } + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathExpression.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathExpression.java new file mode 100644 index 000000000..14daffd93 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/beans/PropertyPathExpression.java @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +/** + * Simple support for Java bean property path expression parsing and evaluation. + * + * Consider a simple Order bean class with a property named {@code customer} of + * type Customer that has its own property named {@code name}. A path expression + * of {@code customer.name}, evaluated on an Order instance will internally make + * a call to the {@code getCustomer()} method on the Order object, and then make + * a call to the {@code getName()} method on the Customer object returned in the + * previous method call. The result of the expression will be the cusomter's + * name. + * + * Instances of {@link PropertyPathExpression} are thread-safe for concurrent + * use across threads with one restriction. A call to {@link #initialize(Class)} + * must be made in advance of concurrent access to ensure that class + * introspection has been completed. + * + * @author bdferris + * + */ +public final class PropertyPathExpression { + + private String[] _properties; + + private transient PropertyMethod[] _methods = null; + + private PropertyMethodResolver _resolver = new DefaultPropertyMethodResolver(); + + /** + * A static convenience method for evaluating a property path expression on a + * target object. If you need to repeatedly evaluate the same property path + * expression, consider creating a {@link PropertyPathExpression} object + * directly so that bean introspection information can be cached. + * + * @param target the target bean instance to evaluate against + * @param query the property path expression to evaluate + * @return the result of the evaluation of the property path expression + */ + public static Object evaluate(Object target, String query) { + return new PropertyPathExpression(query).invoke(target); + } + + /** + * + * @param query the property path expression to evaluate + */ + public PropertyPathExpression(String query) { + _properties = query.split("\\."); + } + + public void setPropertyMethodResolver(PropertyMethodResolver resolver) { + _resolver = resolver; + } + + public String getPath() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < _properties.length; i++) { + if (i > 0) + b.append('.'); + b.append(_properties[i]); + } + return b.toString(); + } + + /** + * Opportunistically complete and cache bean introspection given a source + * value target type. + * + * @param sourceValueType the class of objects that will be passed in calls to + * {@link #invoke(Object)} + * @return the final return type of the evaluated path expression + * @throws IllegalStateException on introspection errors + */ + public Class initialize(Class sourceValueType) { + + if (_methods != null) { + if (_methods.length == 0) + return sourceValueType; + return _methods[_methods.length - 1].getReturnType(); + } + + _methods = new PropertyMethod[_properties.length]; + + for (int i = 0; i < _properties.length; i++) { + _methods[i] = _resolver.getPropertyMethod(sourceValueType, _properties[i]); + sourceValueType = _methods[i].getReturnType(); + } + + return sourceValueType; + } + + /** + * Returns the type of the parent class containing the property to be + * evaluated. For simple property path expressions containing just one + * property, the parent class will be equal to the "sourceValueType" + * parameter. For compound property path expressions, the parent class is + * equal to the class from which the property value will ultimately be + * accessed. + * + * @param sourceValueType + * @return + */ + public Class getParentType(Class sourceValueType) { + initialize(sourceValueType); + if (_methods.length < 2) { + return sourceValueType; + } + return _methods[_methods.length - 2].getReturnType(); + } + + /** + * @return the last property in the compound property path expression + */ + public String getLastProperty() { + return _properties[_properties.length - 1]; + } + + /** + * Invoke the property path expression against the specified object value + * + * @param value the target bean to start the property path expression against + * @return the result of the property path expression evaluation + * @throws IllegalStateException on introspection and evaluation errors + */ + public Object invoke(Object value) { + return invokeReturningFullResult(value).value; + } + + public PropertyInvocationResult invokeReturningFullResult(Object value) { + if (_methods == null) + initialize(value.getClass()); + + Object parent = null; + String propertyName = null; + for (int i = 0; i < _properties.length; i++) { + parent = value; + propertyName = _properties[i]; + PropertyMethod m = _methods[i]; + try { + value = m.invoke(value); + } catch (Exception ex) { + throw new IllegalStateException("error invoking property reader: obj=" + + value + " property=" + _properties[i], ex); + } + } + return new PropertyInvocationResult(parent, propertyName, value); + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/CombinationIterator.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/CombinationIterator.java new file mode 100644 index 000000000..198e7cb62 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/CombinationIterator.java @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.combinations; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +/*********************************************************************************************************************** + * Internal Classes + **********************************************************************************************************************/ + +class CombinationIterator implements Iterator>, Serializable { + + private static final long serialVersionUID = 1L; + + private List _readings; + + private boolean _includeReflexive; + + private int _indexI = 0; + + private int _indexJ = 0; + + private Pair _next = null; + + public CombinationIterator(List readings, boolean includeReflexive) { + + _readings = readings; + _includeReflexive = includeReflexive; + + _indexI = 0; + _indexJ = _includeReflexive ? _indexI : _indexI + 1; + + tryNext(); + } + + public boolean hasNext() { + return _next != null; + } + + public Pair next() { + + if (!hasNext()) + throw new NoSuchElementException(); + + Pair n = _next; + tryNext(); + return n; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + private void tryNext() { + if (_indexI < _readings.size() && _indexJ < _readings.size()) { + _next = Tuples.pair(_readings.get(_indexI), _readings.get(_indexJ)); + _indexJ++; + if (_indexJ >= _readings.size()) { + _indexI++; + _indexJ = _includeReflexive ? _indexI : _indexI + 1; + } + } else { + _next = null; + } + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/Combinations.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/Combinations.java new file mode 100644 index 000000000..bcb5206cd --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/Combinations.java @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.combinations; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.onebusaway.collections.tuple.Pair; + +public abstract class Combinations { + + public static Iterable> getCombinationsReflexive( + Iterable objects) { + return getCombinations(objects, true); + } + + /** + * Given elements {a,b,c} will return {{a,b},{a,c},{b,c}}. + * + * @param + * @param objects + * @return + */ + public static Iterable> getCombinationsNonReflexive( + Iterable objects) { + return getCombinations(objects, false); + } + + public static Iterable> getCombinations(Iterable objects, + boolean includeReflexive) { + List elements = new ArrayList(); + for (T element : objects) + elements.add(element); + return new CombinationsIterable(elements, includeReflexive); + } + + public static Iterable> getPermutations(final Iterable objects) { + return new Iterable>() { + public Iterator> iterator() { + return new PermutationIterator(objects); + } + }; + } + + public static Iterable> getPermutations( + final Iterable objectsA, final Iterable objectsB) { + return new Iterable>() { + public Iterator> iterator() { + return new PermutationIterator(objectsA, objectsB); + } + }; + } + + public static Iterable> getSequentialPairs( + final Iterable objects) { + return new Iterable>() { + public Iterator> iterator() { + return new SequentialPairIterator(objects); + } + }; + } + + public static List> getGroupCombinations(List elements, + int groupSize) { + if (groupSize > elements.size()) + throw new IllegalStateException( + "group size is larger than number of available elements"); + List> lists = new ArrayList>(); + List current = new ArrayList(); + getGroupCombinations(elements, groupSize, 0, lists, current); + return lists; + } + + /******************************************************************************************************************* + * Private Methods + ******************************************************************************************************************/ + + private static void getGroupCombinations(List elements, int groupSize, + int index, List> lists, List current) { + + if (current.size() == groupSize) { + lists.add(current); + return; + } + + int g = groupSize - current.size(); + + for (int i = index; i < elements.size() - g + 1; i++) { + List c = new ArrayList(current.size() + 1); + c.addAll(current); + c.add(elements.get(i)); + getGroupCombinations(elements, groupSize, i + 1, lists, c); + } + } + + private static class CombinationsIterable implements Iterable>, + Serializable { + + private static final long serialVersionUID = 1L; + + private List _elements; + + private boolean _includeReflexive; + + public CombinationsIterable(List elements, boolean includeReflexive) { + _elements = elements; + _includeReflexive = includeReflexive; + } + + public Iterator> iterator() { + return new CombinationIterator(_elements, _includeReflexive); + } + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/PermutationIterator.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/PermutationIterator.java new file mode 100644 index 000000000..fe6e1769c --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/PermutationIterator.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + */ +package org.onebusaway.collections.combinations; + +import java.util.Iterator; + +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +class PermutationIterator implements Iterator> { + + private Iterable _elementsB; + + private Iterator _iteratorA; + + private Iterator _iteratorB; + + private T _elementA = null; + + public PermutationIterator(Iterable elements) { + this(elements, elements); + } + + public PermutationIterator(Iterable elementsA, Iterable elementsB) { + + _iteratorA = elementsA.iterator(); + _iteratorB = elementsB.iterator(); + + _elementsB = elementsB; + + if (_iteratorA.hasNext()) + _elementA = _iteratorA.next(); + } + + public boolean hasNext() { + return _iteratorA.hasNext() || _iteratorB.hasNext(); + } + + public Pair next() { + if (!hasNext()) + throw new IndexOutOfBoundsException(); + + if (!_iteratorB.hasNext()) { + _elementA = _iteratorA.next(); + _iteratorB = _elementsB.iterator(); + } + + return Tuples.pair(_elementA, _iteratorB.next()); + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/SequentialPairIterator.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/SequentialPairIterator.java new file mode 100644 index 000000000..f54e13a1d --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/combinations/SequentialPairIterator.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.combinations; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +public class SequentialPairIterator implements Iterator> { + + private T _prev = null; + + private T _current = null; + + private Iterator _iterator; + + public SequentialPairIterator(Iterable elements) { + _iterator = elements.iterator(); + if (_iterator.hasNext()) + _current = _iterator.next(); + getNext(); + } + + public boolean hasNext() { + return _current != null; + } + + public Pair next() { + if (!hasNext()) + throw new NoSuchElementException(); + Pair pair = Tuples.pair(_prev, _current); + getNext(); + return pair; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + private void getNext() { + _prev = _current; + if (_iterator.hasNext()) + _current = _iterator.next(); + else + _current = null; + } + +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Pair.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Pair.java new file mode 100644 index 000000000..73ecf7f62 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Pair.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +import java.util.NoSuchElementException; + +/** + * An extension of the two-member {@link T2} tuple type where both members of the + * tuple are of the same type. See {@link Tuples#pair(Object, Object)} for a + * factory method to create a Pair object. + * + * @author bdferris + * @see Tuples#pair(Object, Object) + * @see PairImpl + */ +public interface Pair extends T2 { + + /** + * @return true if both members of the pair are equal + */ + public boolean isReflexive(); + + /** + * @param element the target element to test + * @return true if either member of the pair is equal to the target element + */ + public boolean contains(T element); + + /** + * Return {@link #getFirst()} if element equals {@link #getSecond()}, returns + * {@link #getSecond()} if element equals {@link #getFirst()}, and throws + * {@link NoSuchElementException} if element is equal to neither. + * + * @param element + * @return returns + * @throws NoSuchElementException if element is not equal to either member of + * the pair + */ + public T getOpposite(T element) throws NoSuchElementException; + + /** + * @return a Pair whose members are equal to this pair's, but have been + * swapped in order + */ + public Pair swap(); +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/PairImpl.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/PairImpl.java new file mode 100644 index 000000000..31c37a955 --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/PairImpl.java @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +/** + * An implementation class for the {@link Pair} interface. To create an instance + * of {@link Pair}, please use the {@link Tuples#pair(Object, Object)} factory + * method. + * + * @author bdferris + * @see Pair + * @see Tuples#pair(Object, Object) + */ +final class PairImpl implements Pair, Serializable { + + private static final long serialVersionUID = 1L; + + private final T _first; + + private final T _second; + + public PairImpl(T first, T second) { + _first = first; + _second = second; + } + + public T getFirst() { + return _first; + } + + public T getSecond() { + return _second; + } + + public boolean isReflexive() { + return Tuples.equals(_first, _second); + } + + public boolean contains(T element) { + return Tuples.equals(_first, element) || Tuples.equals(_second, element); + } + + public T getOpposite(T element) { + if (Tuples.equals(_first, element)) + return _second; + if (Tuples.equals(_second, element)) + return _first; + throw new NoSuchElementException(); + } + + public PairImpl swap() { + return new PairImpl(_second, _first); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((_first == null) ? 0 : _first.hashCode()); + result = prime * result + ((_second == null) ? 0 : _second.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PairImpl other = (PairImpl) obj; + return Tuples.equals(_first, other._first) + && Tuples.equals(_second, other._second); + } + + @Override + public String toString() { + return "Pair(" + _first + "," + _second + ")"; + } +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2.java new file mode 100644 index 000000000..0fbf8900a --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +/** + * A two-member member tuple class used to represent a collection of two objects + * with unique types. Useful, for example, in cases where you want to return + * more than one object reference from a method. See + * {@link Tuples#tuple(Object, Object)} for a factory method to create a T2 + * object. + * + * @author bdferris + * @see Tuples#tuple(Object, Object) + * @see T2Impl + */ +public interface T2 { + + /** + * @return the first member of the tuple collection + */ + public S1 getFirst(); + + /** + * @return the second member of the tuple collection + */ + public S2 getSecond(); +} \ No newline at end of file diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2Impl.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2Impl.java new file mode 100644 index 000000000..76813908b --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/T2Impl.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +import java.io.Serializable; + +/** + * An implementation class for the {@link T2} interface. To create an instance + * of {@link T2}, please use the {@link Tuples#tuple(Object, Object)} factory + * method. + * + * @author bdferris + * @see T2 + * @see Tuples#tuple(Object, Object) + */ +final class T2Impl implements T2, Serializable { + + private static final long serialVersionUID = 1L; + + private final S1 _first; + + private final S2 _second; + + public T2Impl(S1 first, S2 second) { + _first = first; + _second = second; + } + + public S1 getFirst() { + return _first; + } + + public S2 getSecond() { + return _second; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((_first == null) ? 0 : _first.hashCode()); + result = prime * result + ((_second == null) ? 0 : _second.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + T2Impl other = (T2Impl) obj; + return Tuples.equals(_first, other._first) + && Tuples.equals(_second, other._second); + } + + @Override + public String toString() { + return _first + ", " + _second; + } +} diff --git a/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Tuples.java b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Tuples.java new file mode 100644 index 000000000..ba4793a5c --- /dev/null +++ b/onebusaway-collections/src/main/java/org/onebusaway/collections/tuple/Tuples.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +/** + * A collection of factory methods for creating tuple objects. Tuples are typed + * ordered collection of objects where individual elements can have distinct + * types. We currently support a two-member tuple {@link T2} with distinct + * member types and sub-class {@link Pair} where both member types are the same. + * + * @author bdferris + * @see T2 + * @see Tuple + */ +public abstract class Tuples { + + private Tuples() { + + } + + /** + * A convenience factory method for constructing a {@link Pair} object. + * + * @param first the first member of the pair tuple + * @param second the second member of the pair tuple + * @return a new {@link Pair} object with the specified members + */ + public static Pair pair(T first, T second) { + return new PairImpl(first, second); + } + + /** + * A convenience factory method for constructing a {@link T2} object. + * + * @param first the first member of the tuple + * @param second the second member of the tuple + * @return a new {@link T2} object with the specified members + */ + public static T2 tuple(S1 first, S2 second) { + return new T2Impl(first, second); + } + + /** + * A convenience method to test for object equality that correctly handles + * null objects. + * + * @param a the first object to test for equality + * @param b the second object to test for equality + * @return true if (a == null && b == null) || (a.equals(b)) + */ + public static final boolean equals(Object a, Object b) { + return a == null ? (b == null) : (a.equals(b)); + } +} diff --git a/onebusaway-collections/src/site/apt/index.apt.vm b/onebusaway-collections/src/site/apt/index.apt.vm new file mode 100644 index 000000000..16bc98e41 --- /dev/null +++ b/onebusaway-collections/src/site/apt/index.apt.vm @@ -0,0 +1,31 @@ +onebusaway-collections + + The <<>> module provides a set of common collection and utility classes used by a number of OneBusAway modules. + + <> ${currentVersion} + + Details on all releases can be found in the {{{./release-notes.html}Release Notes}}. + +Documentation + + You can access the {{{./apidocs/index.html}latest Javadoc for the library}}. + +Using in Maven + + The <<>> library is available as a Maven module. Simply add the following dependency: + ++---+ + + + org.onebusaway + onebusaway-collection + ${currentVersion} + + ++---+ + +#if( $currentVersion.contains('SNAPSHOT') ) + To use a SNAPSHOT version of the library, you'll need to {{{https://code.google.com/p/onebusaway/wiki/MavenRepository}add a reference to the OneBusAway Maven repository}}. +#end + + diff --git a/onebusaway-collections/src/site/apt/release-notes.apt.vm b/onebusaway-collections/src/site/apt/release-notes.apt.vm new file mode 100644 index 000000000..ce0cf1365 --- /dev/null +++ b/onebusaway-collections/src/site/apt/release-notes.apt.vm @@ -0,0 +1,60 @@ +Release Notes + +* ${currentVersion} + + * Full Documentation: {{${site_base_url}/onebusaway-collections/${currentVersion}/}} + +* 1.2.0 + + * Move property-path expression code to new {{{./apidocs/org/onebusaway/collections/beans/package-summary.html}org.onebusaway.collections.beans}} + package. + + * Introduce {{{./apidocs/org/onebusaway/collections/beans/PropertyPathCollectionExpression.html}PropertyPathCollectionExpression}}: provides + support for property-path expressions that return multiple values. + + * Introduce {{{./apidocs/org/onebusaway/collections/beans/PropertyMethodResolver.html}PropertyMethodResolver}}: provides support for custom + resolution of property methods in property-path expressions. + + * Full Documentation: {{${site_base_url}/onebusaway-collections/1.2.0/}} + +* 1.1.3 + + * Migration to GitHub. + + * Add additional methods to {{{./apidocs/index.html?org/onebusaway/collections/PropertyPathExpression.html}PropertyPathExpression}}. + + * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.3/}} + +* 1.1.2 + + * Add additional constructors for {{{./apidocs/org/onebusaway/collections/Range.html}Range}} + + * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.2/}} + +* 1.1.1 + + * Documentation tweaks + + * Addition of {{{./apidocs/org/onebusaway/collections/adapter/ListAdapter.html}ListAdapter}} and {{{./apidocs/org/onebusaway/collections/Range.html}Range}} classes + + * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.1/}} + +* 1.1.0 + + * Initial Site Documentation + + * Bump to require Maven 3 + + * FunctionalLibrary - simple filter, grep operations on collections + + * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.0/}} + +* 1.0.4 + +* 1.0.3 + +* 1.0.2 + +* 1.0.1 + +* 1.0.0 diff --git a/onebusaway-collections/src/site/site.xml b/onebusaway-collections/src/site/site.xml new file mode 100644 index 000000000..3a8da01ad --- /dev/null +++ b/onebusaway-collections/src/site/site.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/CollectionsLibraryTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/CollectionsLibraryTest.java new file mode 100644 index 000000000..2343d3e48 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/CollectionsLibraryTest.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.assertEquals; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +public class CollectionsLibraryTest { + + @Test + public void testSet() { + + Set expected = new HashSet(); + expected.add("a"); + expected.add("b"); + expected.add("c"); + + Set actual = CollectionsLibrary.set("a", "b", "c", "a", "b"); + + assertEquals(expected, actual); + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/ConcurrentCollectionsLibraryTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/ConcurrentCollectionsLibraryTest.java new file mode 100644 index 000000000..efeff7d36 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/ConcurrentCollectionsLibraryTest.java @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.junit.Test; + +public class ConcurrentCollectionsLibraryTest { + + @Test + public void testAddToMapValueList() throws InterruptedException { + + ConcurrentMap> m = new ConcurrentHashMap>(); + + List ops = new ArrayList(); + ops.add(new AddOp(m, "a", "1")); + ops.add(new AddOp(m, "b", "1")); + ops.add(new AddOp(m, "a", "2")); + ops.add(new AddOp(m, "a", "1")); + ops.add(new AddOp(m, "b", "2")); + ops.add(new AddOp(m, "b", "3")); + ops.add(new AddOp(m, "a", "3")); + ops.add(new AddOp(m, "a", "3")); + ops.add(new AddOp(m, "a", "4")); + ops.add(new AddOp(m, "a", "5")); + ops.add(new AddOp(m, "a", "6")); + ops.add(new AddOp(m, "a", "7")); + + ExecutorService service = Executors.newFixedThreadPool(5); + service.invokeAll(ops); + + List values = m.get("a"); + assertEquals(7, values.size()); + assertTrue(values.contains("1")); + assertTrue(values.contains("2")); + assertTrue(values.contains("3")); + assertTrue(values.contains("4")); + assertTrue(values.contains("5")); + assertTrue(values.contains("6")); + assertTrue(values.contains("7")); + } + + private static class AddOp implements Callable { + + private ConcurrentMap> _map; + private String _key; + private String _value; + + public AddOp(ConcurrentMap> map, String key, + String value) { + _map = map; + _key = key; + _value = value; + } + + @Override + public String call() throws Exception { + ConcurrentCollectionsLibrary.addToMapValueList(_map, _key, _value); + return _value; + } + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/FactoryMapTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/FactoryMapTest.java new file mode 100644 index 000000000..43c8a3d24 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/FactoryMapTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +public class FactoryMapTest { + + @Test + public void test() { + + FactoryMap> m = new FactoryMap>( + new ArrayList()); + + List list = m.get("a"); + assertEquals(0, list.size()); + list.add("1"); + + list = m.get("b"); + assertEquals(0, list.size()); + list.add("1"); + + list = m.get("a"); + assertEquals(1, list.size()); + list.add("2"); + + list = m.get("b"); + assertEquals(1, list.size()); + assertEquals("1", list.get(0)); + + list = m.get("a"); + assertEquals(2, list.size()); + assertEquals("1", list.get(0)); + assertEquals("2", list.get(1)); + + m.remove("b"); + + list = m.get("b"); + assertEquals(0, list.size()); + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/FunctionalLibraryTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/FunctionalLibraryTest.java new file mode 100644 index 000000000..9b81d8ab4 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/FunctionalLibraryTest.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class FunctionalLibraryTest { + + @Test + public void test() { + + Dummy a = new Dummy("a"); + Dummy b = new Dummy("b"); + Dummy c = new Dummy("c"); + Dummy b2 = new Dummy("b"); + Dummy d = new Dummy("d"); + + List all = Arrays.asList(a, b, c, b2, d); + + List result = FunctionalLibrary.filter(all, "name", "a"); + assertEquals(1, result.size()); + assertSame(a, result.get(0)); + + result = FunctionalLibrary.filter(all, "name", "b"); + assertEquals(2, result.size()); + assertSame(b, result.get(0)); + assertSame(b2, result.get(1)); + + result = FunctionalLibrary.filter(all, "name", "f"); + assertEquals(0, result.size()); + + Dummy r = FunctionalLibrary.filterFirst(all, "name", "a"); + assertSame(a, r); + + r = FunctionalLibrary.filterFirst(all, "name", "b"); + assertSame(b, r); + + r = FunctionalLibrary.filterFirst(all, "name", "f"); + assertNull(r); + } + + public static class Dummy { + private String name; + + public Dummy(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/MappingLibraryTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/MappingLibraryTest.java new file mode 100644 index 000000000..c79de0cce --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/MappingLibraryTest.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +public class MappingLibraryTest { + + @SuppressWarnings("unchecked") + @Test + public void testMap() { + + List> asList = Arrays.asList(Tuples.pair("a", "b"), + Tuples.pair("c", "d"), Tuples.pair("e", "f")); + + List values = MappingLibrary.map(asList, "first"); + assertEquals(3, values.size()); + assertEquals("a", values.get(0)); + assertEquals("c", values.get(1)); + assertEquals("e", values.get(2)); + + values = MappingLibrary.map(asList, "second"); + assertEquals(3, values.size()); + assertEquals("b", values.get(0)); + assertEquals("d", values.get(1)); + assertEquals("f", values.get(2)); + } + + @SuppressWarnings("unchecked") + @Test + public void testMapToValue() { + + Pair p1 = Tuples.pair("a", "1"); + Pair p2 = Tuples.pair("b", "1"); + Pair p3 = Tuples.pair("a", "2"); + List> asList = Arrays.asList(p1, p2, p3); + + Map> values = MappingLibrary.mapToValue(asList, + "first"); + assertEquals(2, values.size()); + assertEquals(p3, values.get("a")); + assertEquals(p2, values.get("b")); + + values = MappingLibrary.mapToValue(asList, "second"); + assertEquals(2, values.size()); + assertEquals(p2, values.get("1")); + assertEquals(p3, values.get("2")); + } + + @SuppressWarnings("unchecked") + @Test + public void testMapToValueList() { + + Pair p1 = Tuples.pair("a", "1"); + Pair p2 = Tuples.pair("b", "1"); + Pair p3 = Tuples.pair("a", "2"); + Pair p4 = Tuples.pair("b", "1"); + + List> asList = Arrays.asList(p1, p2, p3, p4); + + Map>> values = MappingLibrary.mapToValueList( + asList, "first"); + assertEquals(2, values.size()); + assertEquals(Arrays.asList(p1, p3), values.get("a")); + assertEquals(Arrays.asList(p2, p4), values.get("b")); + + values = MappingLibrary.mapToValueList(asList, "second"); + assertEquals(2, values.size()); + assertEquals(Arrays.asList(p1, p2, p4), values.get("1")); + assertEquals(Arrays.asList(p3), values.get("2")); + } + + @SuppressWarnings("unchecked") + @Test + public void testMapToValueSet() { + + Pair p1 = Tuples.pair("a", "1"); + Pair p2 = Tuples.pair("b", "1"); + Pair p3 = Tuples.pair("a", "2"); + Pair p4 = Tuples.pair("b", "1"); + List> asList = Arrays.asList(p1, p2, p3, p4); + + Map>> values = MappingLibrary.mapToValueSet( + asList, "first"); + assertEquals(2, values.size()); + assertEquals(set(p1, p3), values.get("a")); + assertEquals(set(p2), values.get("b")); + + values = MappingLibrary.mapToValueSet(asList, "second"); + assertEquals(2, values.size()); + assertEquals(set(p1, p2), values.get("1")); + assertEquals(set(p3), values.get("2")); + } + + private Set set(T... objects) { + Set set = new HashSet(); + for (T object : objects) + set.add(object); + return set; + } + +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/MinTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/MinTest.java new file mode 100644 index 000000000..97ea05799 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/MinTest.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +public class MinTest { + + @Test + public void test() { + + Min m = new Min(); + + assertTrue(m.isEmpty()); + + m.add(1.0, "a"); + + assertEquals(1.0, m.getMinValue(), 0.0); + assertEquals("a", m.getMinElement()); + List els = m.getMinElements(); + assertEquals(1, els.size()); + assertTrue(els.contains("a")); + + m.add(2.0, "b"); + + assertEquals(1.0, m.getMinValue(), 0.0); + assertEquals("a", m.getMinElement()); + els = m.getMinElements(); + assertEquals(1, els.size()); + assertTrue(els.contains("a")); + + m.add(0.0, "c"); + + assertEquals(0.0, m.getMinValue(), 0.0); + assertEquals("c", m.getMinElement()); + els = m.getMinElements(); + assertEquals(1, els.size()); + assertTrue(els.contains("c")); + + m.add(0.0, "d"); + + assertEquals(0.0, m.getMinValue(), 0.0); + assertEquals("c", m.getMinElement()); + els = m.getMinElements(); + assertEquals(2, els.size()); + assertTrue(els.contains("c")); + assertTrue(els.contains("d")); + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/adapter/AdaptableValueSortedMapTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/adapter/AdaptableValueSortedMapTest.java new file mode 100644 index 000000000..d6153e7d0 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/adapter/AdaptableValueSortedMapTest.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.adapter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.SortedMap; +import java.util.TreeMap; + +import org.junit.Before; +import org.junit.Test; + +public class AdaptableValueSortedMapTest { + + private SortedMap _m2; + private TreeMap _m; + + @Before + public void before() { + + _m = new TreeMap(); + _m.put(1, new TestBean("a")); + _m.put(2, new TestBean("b")); + _m.put(3, new TestBean("c")); + + _m2 = AdapterLibrary.adaptSortedMap(_m, new ValueAdapter()); + } + + @Test + public void testClear() { + _m2.clear(); + assertTrue(_m2.isEmpty()); + assertTrue(_m.isEmpty()); + } + + @Test + public void testContainsKey() { + assertTrue(_m2.containsKey(1)); + assertFalse(_m2.containsKey(4)); + } + + @Test + public void testSubMap() { + SortedMap m = _m2.subMap(1, 2); + assertEquals(1, m.size()); + assertEquals(new Integer(1), m.firstKey()); + assertEquals(new Integer(1), m.lastKey()); + assertEquals("a", m.get(1)); + } + + private static class TestBean { + + private final String _value; + + public TestBean(String value) { + _value = value; + } + } + + private static class ValueAdapter implements IAdapter { + + @Override + public String adapt(TestBean source) { + return source._value; + } + + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathCollectionExpressionTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathCollectionExpressionTest.java new file mode 100644 index 000000000..df9b392ad --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathCollectionExpressionTest.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.onebusaway.collections.beans.PropertyPathCollectionExpression; + +public class PropertyPathCollectionExpressionTest { + + @Test + public void test() { + TestObject a = new TestObject("a"); + TestObject b = new TestObject("b"); + TestObject c = new TestObject("c"); + TestObject d = new TestObject("d"); + a.setValues(Arrays.asList(b, c, d)); + b.setValues(Arrays.asList(c, a)); + c.setValues(Arrays.asList(d)); + + assertEquals(Arrays.asList("b", "c", "d"), + PropertyPathCollectionExpression.evaluate(a, "values.value")); + assertEquals(Arrays.asList("c", "a", "d"), + PropertyPathCollectionExpression.evaluate(a, "values.values.value")); + } + + @Test + public void testFullResult() { + TestObject a = new TestObject("a"); + TestObject b = new TestObject("b"); + a.setValues(Arrays.asList(b)); + + { + PropertyPathCollectionExpression expression = new PropertyPathCollectionExpression( + "values"); + List results = new ArrayList(); + expression.invokeReturningFullResult(a, results); + assertEquals(1, results.size()); + PropertyInvocationResult result = results.get(0); + assertSame(a, result.parent); + assertEquals("values", result.propertyName); + assertSame(b, result.value); + } + + { + PropertyPathCollectionExpression expression = new PropertyPathCollectionExpression( + "values.value"); + List results = new ArrayList(); + expression.invokeReturningFullResult(a, results); + assertEquals(1, results.size()); + PropertyInvocationResult result = results.get(0); + assertSame(b, result.parent); + assertEquals("value", result.propertyName); + assertSame("b", result.value); + } + } + + public static class TestObject { + + private String value; + + private List values = new ArrayList(); + + public TestObject(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathExpressionTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathExpressionTest.java new file mode 100644 index 000000000..e2a4d04aa --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/beans/PropertyPathExpressionTest.java @@ -0,0 +1,156 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.beans; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.onebusaway.collections.beans.PropertyPathExpression; + +public class PropertyPathExpressionTest { + + @Test + public void test() { + + A obj = new A(); + + PropertyPathExpression expr = new PropertyPathExpression("stringValue"); + assertEquals("string-value", expr.invoke(obj)); + + expr = new PropertyPathExpression("integerValue"); + assertEquals(new Integer(31), expr.invoke(obj)); + + expr = new PropertyPathExpression("doubleValue"); + assertEquals(new Double(3.14), expr.invoke(obj)); + + expr = new PropertyPathExpression("nullA"); + assertNull(expr.invoke(obj)); + + expr = new PropertyPathExpression("depth"); + assertEquals(new Integer(0), expr.invoke(obj)); + + expr = new PropertyPathExpression("a.depth"); + assertEquals(new Integer(1), expr.invoke(obj)); + + expr = new PropertyPathExpression("a.a.depth"); + assertEquals(new Integer(2), expr.invoke(obj)); + + expr = new PropertyPathExpression("a.doubleValue"); + assertEquals(new Double(3.14), expr.invoke(obj)); + + expr = new PropertyPathExpression("dne"); + + try { + expr.invoke(obj); + fail(); + } catch (IllegalStateException ex) { + + } + + expr = new PropertyPathExpression("nullA.depth"); + + try { + expr.invoke(obj); + fail(); + } catch (IllegalStateException ex) { + + } + + assertEquals(new Double(3.14), + PropertyPathExpression.evaluate(obj, "a.doubleValue")); + + } + + @Test + public void testGetParentType() { + PropertyPathExpression exp = new PropertyPathExpression("a"); + assertEquals(A.class, exp.getParentType(A.class)); + + exp = new PropertyPathExpression("a.a"); + assertEquals(A.class, exp.getParentType(A.class)); + + exp = new PropertyPathExpression("b.a"); + assertEquals(B.class, exp.getParentType(A.class)); + + exp = new PropertyPathExpression("a.b.a"); + assertEquals(B.class, exp.getParentType(A.class)); + } + + public static class Base { + + private final int _depth; + + public Base() { + this(0); + } + + public Base(int depth) { + _depth = depth; + } + + public int getDepth() { + return _depth; + } + + public A getA() { + return new A(_depth + 1); + } + + public B getB() { + return new B(_depth + 1); + } + } + + public static class A extends Base { + + public A() { + super(); + } + + public A(int depth) { + super(depth); + } + + public String getStringValue() { + return "string-value"; + } + + public int getIntegerValue() { + return 31; + } + + public double getDoubleValue() { + return 3.14; + } + + public A getNullA() { + return null; + } + } + + public static class B extends Base { + + public B() { + super(); + } + + public B(int depth) { + super(depth); + } + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/combinations/CombinationsTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/combinations/CombinationsTest.java new file mode 100644 index 000000000..a23352bb2 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/combinations/CombinationsTest.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.combinations; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.junit.Test; +import org.onebusaway.collections.tuple.Pair; +import org.onebusaway.collections.tuple.Tuples; + +public class CombinationsTest { + + @Test + public void testGetSequentialPairs() { + List values = Arrays.asList("a", "b", "c"); + Iterable> iterable = Combinations.getSequentialPairs(values); + Iterator> it = iterable.iterator(); + assertEquals(Tuples.pair("a", "b"), it.next()); + assertEquals(Tuples.pair("b", "c"), it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void testCominationsReflexive() { + List values = Arrays.asList("a", "b", "c"); + Iterator> it = Combinations.getCombinationsReflexive(values).iterator(); + assertEquals(Tuples.pair("a", "a"), it.next()); + assertEquals(Tuples.pair("a", "b"), it.next()); + assertEquals(Tuples.pair("a", "c"), it.next()); + assertEquals(Tuples.pair("b", "b"), it.next()); + assertEquals(Tuples.pair("b", "c"), it.next()); + assertEquals(Tuples.pair("c", "c"), it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void testCominationsNonReflexive() { + List values = Arrays.asList("a", "b", "c"); + Iterator> it = Combinations.getCombinationsNonReflexive(values).iterator(); + assertEquals(Tuples.pair("a", "b"), it.next()); + assertEquals(Tuples.pair("a", "c"), it.next()); + assertEquals(Tuples.pair("b", "c"), it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void testPermutations() { + List values = Arrays.asList("a", "b", "c"); + Iterator> it = Combinations.getPermutations(values).iterator(); + assertEquals(Tuples.pair("a", "a"), it.next()); + assertEquals(Tuples.pair("a", "b"), it.next()); + assertEquals(Tuples.pair("a", "c"), it.next()); + assertEquals(Tuples.pair("b", "a"), it.next()); + assertEquals(Tuples.pair("b", "b"), it.next()); + assertEquals(Tuples.pair("b", "c"), it.next()); + assertEquals(Tuples.pair("c", "a"), it.next()); + assertEquals(Tuples.pair("c", "b"), it.next()); + assertEquals(Tuples.pair("c", "c"), it.next()); + assertFalse(it.hasNext()); + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/PairImplTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/PairImplTest.java new file mode 100644 index 000000000..c91510992 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/PairImplTest.java @@ -0,0 +1,156 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.NoSuchElementException; + +import org.junit.Test; + +public class PairImplTest { + + @Test + public void testFirstAndSecond() { + + PairImpl p1 = new PairImpl("a", "b"); + assertEquals("a", p1.getFirst()); + assertEquals("b", p1.getSecond()); + + PairImpl p2 = new PairImpl(null, "b"); + assertEquals(null, p2.getFirst()); + assertEquals("b", p2.getSecond()); + + PairImpl p3 = new PairImpl("a", null); + assertEquals("a", p3.getFirst()); + assertEquals(null, p3.getSecond()); + + PairImpl p4 = new PairImpl(null, null); + assertEquals(null, p4.getFirst()); + assertEquals(null, p4.getSecond()); + } + + @Test + public void testContains() { + + PairImpl p1 = new PairImpl("a", "b"); + + assertTrue(p1.contains("a")); + assertTrue(p1.contains("b")); + assertFalse(p1.contains("c")); + assertFalse(p1.contains(null)); + + PairImpl p2 = new PairImpl(null, "b"); + + assertFalse(p2.contains("a")); + assertTrue(p2.contains("b")); + assertFalse(p2.contains("c")); + assertTrue(p2.contains(null)); + + PairImpl p3 = new PairImpl(null, null); + + assertFalse(p3.contains("a")); + assertFalse(p3.contains("b")); + assertFalse(p3.contains("c")); + assertTrue(p3.contains(null)); + } + + @Test + public void testGetOpposite() { + + PairImpl p1 = new PairImpl("a", "b"); + + assertEquals("b", p1.getOpposite("a")); + assertEquals("a", p1.getOpposite("b")); + + try { + p1.getOpposite("c"); + fail(); + } catch (NoSuchElementException ex) { + + } + + try { + p1.getOpposite(null); + fail(); + } catch (NoSuchElementException ex) { + + } + + PairImpl p2 = new PairImpl(null, "b"); + assertEquals("b", p2.getOpposite(null)); + assertEquals(null, p2.getOpposite("b")); + } + + @Test + public void testIsReflexibe() { + + PairImpl p1 = new PairImpl("a", "b"); + assertFalse(p1.isReflexive()); + + PairImpl p2 = new PairImpl("a", "a"); + assertTrue(p2.isReflexive()); + + PairImpl p3 = new PairImpl(null, "a"); + assertFalse(p3.isReflexive()); + + PairImpl p4 = new PairImpl("a", null); + assertFalse(p4.isReflexive()); + + PairImpl p5 = new PairImpl(null, null); + assertTrue(p5.isReflexive()); + } + + @Test + public void testSwap() { + PairImpl p1 = new PairImpl("a", "b"); + PairImpl p2 = p1.swap(); + assertEquals("b", p2.getFirst()); + assertEquals("a", p2.getSecond()); + + PairImpl p3 = new PairImpl(null, "b"); + PairImpl p4 = p3.swap(); + assertEquals("b", p4.getFirst()); + assertEquals(null, p4.getSecond()); + } + + @Test + public void testEquality() { + PairImpl p1 = new PairImpl("a", "b"); + PairImpl p2 = new PairImpl("a", "b"); + PairImpl p3 = new PairImpl(null, "b"); + PairImpl p4 = new PairImpl(null, "b"); + PairImpl p5 = new PairImpl(null, null); + PairImpl p6 = new PairImpl(null, null); + PairImpl p7 = new PairImpl("c", "b"); + + assertEquals(p1, p2); + assertFalse(p2.equals(p3)); + assertEquals(p3, p4); + assertFalse(p1.equals(p7)); + + assertFalse(p1.equals(null)); + assertFalse(p1.equals("a")); + assertEquals(p1, p1); + + assertEquals(p1.hashCode(), p2.hashCode()); + assertEquals(p3.hashCode(), p4.hashCode()); + assertEquals(p5.hashCode(), p6.hashCode()); + } +} diff --git a/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/T2ImplTest.java b/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/T2ImplTest.java new file mode 100644 index 000000000..3daf702a5 --- /dev/null +++ b/onebusaway-collections/src/test/java/org/onebusaway/collections/tuple/T2ImplTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.collections.tuple; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +public class T2ImplTest { + + @Test + public void testFirstAndSecond() { + + T2Impl p1 = new T2Impl("a", "b"); + assertEquals("a", p1.getFirst()); + assertEquals("b", p1.getSecond()); + + T2Impl p2 = new T2Impl(null, "b"); + assertEquals(null, p2.getFirst()); + assertEquals("b", p2.getSecond()); + + T2Impl p3 = new T2Impl("a", null); + assertEquals("a", p3.getFirst()); + assertEquals(null, p3.getSecond()); + + T2Impl p4 = new T2Impl(null, null); + assertEquals(null, p4.getFirst()); + assertEquals(null, p4.getSecond()); + } + + @Test + public void testEquality() { + T2Impl p1 = new T2Impl("a", "b"); + T2Impl p2 = new T2Impl("a", "b"); + T2Impl p3 = new T2Impl(null, "b"); + T2Impl p4 = new T2Impl(null, "b"); + T2Impl p5 = new T2Impl(null, null); + T2Impl p6 = new T2Impl(null, null); + T2Impl p7 = new T2Impl("c", "b"); + + assertEquals(p1, p1); + assertEquals(p1, p2); + assertFalse(p2.equals(p3)); + assertEquals(p3, p4); + assertFalse(p1.equals(p7)); + assertEquals(p5, p6); + assertFalse(p1.equals(null)); + assertFalse(p1.equals("a")); + + assertEquals(p1.hashCode(), p2.hashCode()); + assertEquals(p3.hashCode(), p4.hashCode()); + assertEquals(p5.hashCode(), p6.hashCode()); + + } +} diff --git a/pom.xml b/pom.xml index b92d9e170..816245679 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,7 @@ + onebusaway-collections onebusaway-gtfs onebusaway-gtfs-hibernate onebusaway-gtfs-hibernate-cli @@ -236,4 +237,5 @@ + From 522ff3e6e64a2923d59b788efec777d7ab4e9c1c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:36:17 +0200 Subject: [PATCH 105/234] Fix maven setup --- .../src/site/apt/index.apt.vm | 31 ---------- .../src/site/apt/release-notes.apt.vm | 60 ------------------- onebusaway-collections/src/site/site.xml | 14 ----- onebusaway-gtfs-merge/pom.xml | 3 +- onebusaway-gtfs-transformer/pom.xml | 7 +-- pom.xml | 12 ---- 6 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 onebusaway-collections/src/site/apt/index.apt.vm delete mode 100644 onebusaway-collections/src/site/apt/release-notes.apt.vm delete mode 100644 onebusaway-collections/src/site/site.xml diff --git a/onebusaway-collections/src/site/apt/index.apt.vm b/onebusaway-collections/src/site/apt/index.apt.vm deleted file mode 100644 index 16bc98e41..000000000 --- a/onebusaway-collections/src/site/apt/index.apt.vm +++ /dev/null @@ -1,31 +0,0 @@ -onebusaway-collections - - The <<>> module provides a set of common collection and utility classes used by a number of OneBusAway modules. - - <> ${currentVersion} - - Details on all releases can be found in the {{{./release-notes.html}Release Notes}}. - -Documentation - - You can access the {{{./apidocs/index.html}latest Javadoc for the library}}. - -Using in Maven - - The <<>> library is available as a Maven module. Simply add the following dependency: - -+---+ - - - org.onebusaway - onebusaway-collection - ${currentVersion} - - -+---+ - -#if( $currentVersion.contains('SNAPSHOT') ) - To use a SNAPSHOT version of the library, you'll need to {{{https://code.google.com/p/onebusaway/wiki/MavenRepository}add a reference to the OneBusAway Maven repository}}. -#end - - diff --git a/onebusaway-collections/src/site/apt/release-notes.apt.vm b/onebusaway-collections/src/site/apt/release-notes.apt.vm deleted file mode 100644 index ce0cf1365..000000000 --- a/onebusaway-collections/src/site/apt/release-notes.apt.vm +++ /dev/null @@ -1,60 +0,0 @@ -Release Notes - -* ${currentVersion} - - * Full Documentation: {{${site_base_url}/onebusaway-collections/${currentVersion}/}} - -* 1.2.0 - - * Move property-path expression code to new {{{./apidocs/org/onebusaway/collections/beans/package-summary.html}org.onebusaway.collections.beans}} - package. - - * Introduce {{{./apidocs/org/onebusaway/collections/beans/PropertyPathCollectionExpression.html}PropertyPathCollectionExpression}}: provides - support for property-path expressions that return multiple values. - - * Introduce {{{./apidocs/org/onebusaway/collections/beans/PropertyMethodResolver.html}PropertyMethodResolver}}: provides support for custom - resolution of property methods in property-path expressions. - - * Full Documentation: {{${site_base_url}/onebusaway-collections/1.2.0/}} - -* 1.1.3 - - * Migration to GitHub. - - * Add additional methods to {{{./apidocs/index.html?org/onebusaway/collections/PropertyPathExpression.html}PropertyPathExpression}}. - - * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.3/}} - -* 1.1.2 - - * Add additional constructors for {{{./apidocs/org/onebusaway/collections/Range.html}Range}} - - * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.2/}} - -* 1.1.1 - - * Documentation tweaks - - * Addition of {{{./apidocs/org/onebusaway/collections/adapter/ListAdapter.html}ListAdapter}} and {{{./apidocs/org/onebusaway/collections/Range.html}Range}} classes - - * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.1/}} - -* 1.1.0 - - * Initial Site Documentation - - * Bump to require Maven 3 - - * FunctionalLibrary - simple filter, grep operations on collections - - * Full Documentation: {{${site_base_url}/onebusaway-collections/1.1.0/}} - -* 1.0.4 - -* 1.0.3 - -* 1.0.2 - -* 1.0.1 - -* 1.0.0 diff --git a/onebusaway-collections/src/site/site.xml b/onebusaway-collections/src/site/site.xml deleted file mode 100644 index 3a8da01ad..000000000 --- a/onebusaway-collections/src/site/site.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 6350c8c33..54f1f0606 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -19,9 +19,8 @@ org.onebusaway onebusaway-collections + ${project.version} - - org.slf4j slf4j-simple diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 5339980a9..a60d23270 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -21,12 +21,7 @@ org.onebusaway onebusaway-collections - - - junit - junit - - + ${project.version} org.json diff --git a/pom.xml b/pom.xml index 816245679..fd1f53dbd 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,6 @@ UTF-8 UTF-8 1.1.7 - 1.2.8 2.0.16 0.0.13 5.11.0 @@ -92,17 +91,6 @@ - - org.onebusaway - onebusaway-collections - ${onebusaway_collections_version} - - - junit - junit - - - org.slf4j slf4j From 1d5092c614701b163a158a0ec8f3e5dbe2b5005a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:45:15 +0200 Subject: [PATCH 106/234] Add onebusaway-csv-entities --- onebusaway-csv-entities/README.md | 3 + onebusaway-csv-entities/pom.xml | 44 ++ .../onebusaway/csv_entities/CSVLibrary.java | 133 +++++ .../onebusaway/csv_entities/CSVListener.java | 22 + .../csv_entities/CsvEntityContext.java | 23 + .../csv_entities/CsvEntityContextImpl.java | 32 ++ .../csv_entities/CsvEntityReader.java | 228 ++++++++ .../csv_entities/CsvEntityWriter.java | 77 +++ .../csv_entities/CsvEntityWriterFactory.java | 61 +++ .../csv_entities/CsvInputSource.java | 25 + .../csv_entities/CsvTokenizerStrategy.java | 41 ++ .../csv_entities/DelimitedTextParser.java | 139 +++++ .../DelimiterTokenizerStrategy.java | 66 +++ .../csv_entities/EntityHandler.java | 20 + .../csv_entities/FileCsvInputSource.java | 44 ++ .../csv_entities/FileOutputStrategy.java | 86 ++++ .../csv_entities/HasExtensions.java | 101 ++++ .../IndividualCsvEntityReader.java | 159 ++++++ .../IndividualCsvEntityWriter.java | 118 +++++ .../csv_entities/ListEntityHandler.java | 44 ++ .../csv_entities/OutputStrategy.java | 38 ++ .../csv_entities/TokenizerStrategy.java | 23 + .../csv_entities/ZipFileCsvInputSource.java | 44 ++ .../csv_entities/ZipOutputStrategy.java | 125 +++++ .../exceptions/CsvEntityException.java | 51 ++ .../exceptions/CsvEntityIOException.java | 49 ++ .../csv_entities/exceptions/CsvException.java | 42 ++ .../EntityInstantiationException.java | 32 ++ .../exceptions/IntrospectionException.java | 38 ++ .../InvalidValueEntityException.java | 48 ++ .../exceptions/MethodInvocationException.java | 35 ++ .../MissingRequiredEntityException.java | 46 ++ .../MissingRequiredFieldException.java | 44 ++ .../NoCsvFieldsAnnotationException.java | 38 ++ .../NoDefaultConverterException.java | 56 ++ .../exceptions/NoSuchPropertyException.java | 46 ++ .../AbstractEntitySchemaFactoryImpl.java | 486 ++++++++++++++++++ .../schema/AbstractEntityValidator.java | 42 ++ .../schema/AbstractFieldMapping.java | 162 ++++++ .../AnnotationDrivenEntitySchemaFactory.java | 303 +++++++++++ .../csv_entities/schema/BaseEntitySchema.java | 70 +++ .../csv_entities/schema/BeanWrapper.java | 50 ++ .../schema/BeanWrapperFactory.java | 124 +++++ .../schema/DateFieldMappingFactory.java | 146 ++++++ .../schema/DecimalFieldMappingFactory.java | 139 +++++ .../csv_entities/schema/DefaultConverter.java | 28 + .../schema/DefaultEntitySchemaFactory.java | 79 +++ .../schema/DefaultFieldMapping.java | 72 +++ .../csv_entities/schema/EntitySchema.java | 57 ++ .../schema/EntitySchemaFactory.java | 22 + .../schema/EntitySchemaFactoryHelper.java | 142 +++++ .../csv_entities/schema/EntityValidator.java | 31 ++ .../schema/EntityValidatorFactory.java | 20 + .../schema/EnumFieldMappingFactory.java | 57 ++ ...OptionalAndMissingEntitySchemaFactory.java | 112 ++++ .../schema/ExtensionEntitySchema.java | 28 + .../csv_entities/schema/FieldMapping.java | 43 ++ .../schema/FieldMappingFactory.java | 23 + .../schema/FlattenFieldMapping.java | 73 +++ .../schema/FlattenFieldMappingFactory.java | 26 + .../schema/ListableCsvMappingFactory.java | 24 + .../schema/SingleFieldMapping.java | 38 ++ .../schema/annotations/CsvField.java | 120 +++++ .../annotations/CsvFieldNameConvention.java | 47 ++ .../schema/annotations/CsvFields.java | 80 +++ .../schema/beans/CsvEntityMappingBean.java | 162 ++++++ .../schema/beans/CsvFieldMappingBean.java | 138 +++++ .../src/main/javadoc/overview.html | 115 +++++ .../csv_entities/AnnotatedTestBean.java | 45 ++ .../csv_entities/CSVLibraryTest.java | 136 +++++ .../csv_entities/CsvEntityReaderTest.java | 75 +++ .../csv_entities/ExtensionsTest.java | 182 +++++++ .../csv_entities/HasExtensionsImpl.java | 38 ++ .../IndividualCsvEntityWriterTest.java | 121 +++++ .../csv_entities/OptionalFieldTestBean.java | 68 +++ .../org/onebusaway/csv_entities/TestBean.java | 39 ++ .../AbstractEntitySchemaFactoryImplTest.java | 98 ++++ .../schema/BeanWrapperFactoryTest.java | 77 +++ .../DecimalFieldMappingFactoryTest.java | 80 +++ 79 files changed, 6239 insertions(+) create mode 100644 onebusaway-csv-entities/README.md create mode 100644 onebusaway-csv-entities/pom.xml create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVLibrary.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVListener.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContext.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContextImpl.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityReader.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriter.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriterFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvInputSource.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvTokenizerStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimitedTextParser.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimiterTokenizerStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/EntityHandler.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileCsvInputSource.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileOutputStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/HasExtensions.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityReader.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ListEntityHandler.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/OutputStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/TokenizerStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipFileCsvInputSource.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipOutputStrategy.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityIOException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/EntityInstantiationException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/IntrospectionException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/InvalidValueEntityException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MethodInvocationException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredEntityException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredFieldException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoCsvFieldsAnnotationException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoDefaultConverterException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoSuchPropertyException.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImpl.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntityValidator.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractFieldMapping.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AnnotationDrivenEntitySchemaFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BaseEntitySchema.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapper.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapperFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DateFieldMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultConverter.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultEntitySchemaFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultFieldMapping.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchema.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactoryHelper.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntityValidator.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntityValidatorFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EnumFieldMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExcludeOptionalAndMissingEntitySchemaFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExtensionEntitySchema.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMapping.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMapping.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ListableCsvMappingFactory.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/SingleFieldMapping.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvField.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFieldNameConvention.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFields.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvEntityMappingBean.java create mode 100644 onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvFieldMappingBean.java create mode 100644 onebusaway-csv-entities/src/main/javadoc/overview.html create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/AnnotatedTestBean.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CSVLibraryTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CsvEntityReaderTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/ExtensionsTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/HasExtensionsImpl.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/IndividualCsvEntityWriterTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/OptionalFieldTestBean.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/TestBean.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImplTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/BeanWrapperFactoryTest.java create mode 100644 onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactoryTest.java diff --git a/onebusaway-csv-entities/README.md b/onebusaway-csv-entities/README.md new file mode 100644 index 000000000..7130329a8 --- /dev/null +++ b/onebusaway-csv-entities/README.md @@ -0,0 +1,3 @@ +# onebusaway-csv-entities + +Provides a library for reading and writing Java objects from CSV files. diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml new file mode 100644 index 000000000..fff566b9a --- /dev/null +++ b/onebusaway-csv-entities/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + + + org.onebusaway + onebusaway-gtfs-modules + 3.1.1-SNAPSHOT + ../pom.xml + + + onebusaway-csv-entities + jar + + onebusaway-csv-entities + A Java library for reading and writing Java objects from comma-separated-values files. + https://github.com/OneBusAway/onebusaway-gtfs-modules/onebusaway-csv-entities/ + + + + commons-beanutils + commons-beanutils + 1.7.0 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + junit + junit + 4.13.2 + test + + + org.mockito + mockito-core + 5.13.0 + test + + + + diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVLibrary.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVLibrary.java new file mode 100644 index 000000000..e607b0f15 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVLibrary.java @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +/** + * Why do we have our own CSV reader when there are a couple of existing Java + * libraries? Mostly because we need to be able to handle some malformed CSV + * that most parsers would choke on but that agencies produce and google seems + * to validate as well. + * + * @author bdferris + * + */ +public class CSVLibrary { + + private final DelimitedTextParser _parser = new DelimitedTextParser(','); + + public static String escapeValue(String value) { + if (value.indexOf(',') != -1 || value.indexOf('"') != -1) + value = "\"" + value.replaceAll("\"", "\"\"") + "\""; + return value; + } + + public static String getArrayAsCSV(double[] args) { + StringBuilder csv = new StringBuilder(); + boolean seenFirst = false; + for (double v : args) { + if (seenFirst) + csv.append(','); + else + seenFirst = false; + csv.append(v); + } + return csv.toString(); + } + + public static String getArrayAsCSV(T[] args) { + StringBuilder csv = new StringBuilder(); + boolean seenFirst = false; + for (T v : args) { + if (seenFirst) + csv.append(','); + else + seenFirst = true; + csv.append(escapeValue(v.toString())); + } + return csv.toString(); + } + + public static String getIterableAsCSV(Iterable args) { + StringBuilder csv = new StringBuilder(); + boolean seenFirst = false; + for (T v : args) { + if (seenFirst) + csv.append(','); + else + seenFirst = true; + csv.append(escapeValue(v.toString())); + } + return csv.toString(); + } + + public static String getAsCSV(Object... args) { + StringBuilder csv = new StringBuilder(); + boolean seenFirst = false; + for (Object v : args) { + if (seenFirst) + csv.append(','); + else + seenFirst = true; + csv.append(escapeValue(v.toString())); + } + return csv.toString(); + } + + public void setTrimInitialWhitespace(boolean trimInitialWhitespace) { + _parser.setTrimInitialWhitespace(trimInitialWhitespace); + } + + public final void parse(InputStream is, CSVListener handler) throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + parse(reader, handler); + } + + public final void parse(File input, CSVListener handler) throws Exception { + BufferedReader r = new BufferedReader(new FileReader(input)); + parse(r, handler); + } + + public void parse(BufferedReader r, CSVListener handler) throws IOException, + Exception { + String line = null; + int lineNumber = 1; + + while ((line = r.readLine()) != null) { + List values = parse(line); + try { + handler.handleLine(values); + } catch (Exception ex) { + throw new Exception("error handling csv record for lineNumber=" + + lineNumber, ex); + } + lineNumber++; + } + + r.close(); + } + + public final List parse(String line) { + return _parser.parse(line); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVListener.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVListener.java new file mode 100644 index 000000000..0282ad9ab --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CSVListener.java @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.List; + +public interface CSVListener { + public void handleLine(List line) throws Exception; +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContext.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContext.java new file mode 100644 index 000000000..b5b81cab9 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContext.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +public interface CsvEntityContext { + + public Object put(Object key, Object value); + + public Object get(Object key); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContextImpl.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContextImpl.java new file mode 100644 index 000000000..1d0f61c4e --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityContextImpl.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.HashMap; +import java.util.Map; + +public class CsvEntityContextImpl implements CsvEntityContext { + + private Map _params = new HashMap(); + + public Object get(Object key) { + return _params.get(key); + } + + public Object put(Object key, Object value) { + return _params.put(key, value); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityReader.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityReader.java new file mode 100644 index 000000000..d4d18f774 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityReader.java @@ -0,0 +1,228 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipFile; + +import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; +import org.onebusaway.csv_entities.exceptions.MissingRequiredEntityException; +import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +public class CsvEntityReader { + + public static final String KEY_CONTEXT = CsvEntityReader.class.getName() + + ".context"; + + private EntitySchemaFactory _entitySchemaFactory = new DefaultEntitySchemaFactory(); + + private EntityHandlerImpl _handler = new EntityHandlerImpl(); + + private CsvEntityContextImpl _context = new CsvEntityContextImpl(); + + private CsvInputSource _source; + + private TokenizerStrategy _tokenizerStrategy = new CsvTokenizerStrategy(); + + private List _handlers = new ArrayList(); + + private boolean _trimValues = false; + + private boolean _internStrings = false; + + private Map _stringTable = new HashMap(); + + /** + * @return the {@link EntitySchemaFactory} that will be used for introspection + * of bean classes + */ + public EntitySchemaFactory getEntitySchemaFactory() { + return _entitySchemaFactory; + } + + public void setEntitySchemaFactory(EntitySchemaFactory entitySchemaFactory) { + _entitySchemaFactory = entitySchemaFactory; + } + + public CsvInputSource getInputSource() { + return _source; + } + + public void setInputSource(CsvInputSource source) { + _source = source; + } + + public void setInputLocation(File path) throws IOException { + if (path.isDirectory()) + _source = new FileCsvInputSource(path); + else + _source = new ZipFileCsvInputSource(new ZipFile(path)); + } + + public void setTokenizerStrategy(TokenizerStrategy tokenizerStrategy) { + _tokenizerStrategy = tokenizerStrategy; + } + + public void setTrimValues(boolean trimValues) { + _trimValues = trimValues; + } + + public void addEntityHandler(EntityHandler handler) { + _handlers.add(handler); + } + + public CsvEntityContext getContext() { + return _context; + } + + public void setInternStrings(boolean internStrings) { + _internStrings = internStrings; + } + + public void readEntities(Class entityClass) throws IOException { + readEntities(entityClass, _source); + } + + public void readEntities(Class entityClass, CsvInputSource source) + throws IOException { + InputStream is = openInputStreamForEntityClass(source, entityClass); + if (is != null) + readEntities(entityClass, is); + } + + public void readEntities(Class entityClass, InputStream is) + throws IOException, CsvEntityIOException { + readEntities(entityClass, new InputStreamReader(is, "UTF-8")); + } + + public void readEntities(Class entityClass, Reader reader) + throws IOException, CsvEntityIOException { + + EntitySchema schema = _entitySchemaFactory.getSchema(entityClass); + + IndividualCsvEntityReader entityLoader = createIndividualCsvEntityReader( + _context, schema, _handler); + entityLoader.setTrimValues(_trimValues); + + BufferedReader lineReader = new BufferedReader(reader); + + /** + * Skip the initial UTF BOM, if present + */ + lineReader.mark(1); + int c = lineReader.read(); + + if (c != 0xFEFF) { + lineReader.reset(); + } + + String line = null; + int lineNumber = 1; + + try { + while ((line = lineReader.readLine()) != null) { + if (line.isEmpty()) + continue; + // TODO: This is a hack of sorts to deal with a malformed data file... + if (line.length() == 1 && line.charAt(0) == 26) + continue; + List values = _tokenizerStrategy.parse(line); + if (_internStrings) + internStrings(values); + entityLoader.handleLine(values); + lineNumber++; + } + } catch (Exception ex) { + throw new CsvEntityIOException(entityClass, schema.getFilename(), + lineNumber, ex); + } finally { + try { + lineReader.close(); + } catch (IOException ex) { + + } + } + } + + protected IndividualCsvEntityReader createIndividualCsvEntityReader( + CsvEntityContext context, EntitySchema schema, EntityHandler handler) { + return new IndividualCsvEntityReader(context, schema, handler); + } + + /** + * Sometimes it may be necessary to inject an instantiated entity directly + * instead of loading it from a CSV source. This method allows you to add a + * new entity, with all handlers called for that entity as if it had just been + * read from a source. + * + * @param entity the entity to be injected + */ + public void injectEntity(Object entity) { + _handler.handleEntity(entity); + } + + public InputStream openInputStreamForEntityClass(CsvInputSource source, + Class entityClass) throws IOException { + + EntitySchema schema = _entitySchemaFactory.getSchema(entityClass); + + String name = schema.getFilename(); + if (!_source.hasResource(name)) { + if (schema.isRequired()) + throw new MissingRequiredEntityException(entityClass, name); + return null; + } + + return _source.getResource(name); + } + + public void close() throws IOException { + if (_source != null) + _source.close(); + } + + private void internStrings(List values) { + for (int i = 0; i < values.size(); i++) { + String value = values.get(i); + String existing = _stringTable.get(value); + if (existing != null) { + values.set(i, existing); + } else { + _stringTable.put(value, value); + } + } + } + + private class EntityHandlerImpl implements EntityHandler { + + public void handleEntity(Object entity) { + for (EntityHandler handler : _handlers) + handler.handleEntity(entity); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriter.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriter.java new file mode 100644 index 000000000..d1b4939ad --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriter.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.File; +import java.io.IOException; + +import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; +import org.onebusaway.csv_entities.schema.ExcludeOptionalAndMissingEntitySchemaFactory; + +public class CsvEntityWriter implements EntityHandler { + + private EntitySchemaFactory _entitySchemaFactory = new DefaultEntitySchemaFactory(); + + private ExcludeOptionalAndMissingEntitySchemaFactory _excludeOptionalAndMissing = null; + + private CsvEntityContext _context = new CsvEntityContextImpl(); + + private OutputStrategy _outputStrategy = null; + + public EntitySchemaFactory getEntitySchemaFactory() { + return _entitySchemaFactory; + } + + public void setEntitySchemaFactory(EntitySchemaFactory entitySchemaFactory) { + _entitySchemaFactory = entitySchemaFactory; + } + + public void setOutputLocation(File path) { + if (path.getName().endsWith(".zip")) { + _outputStrategy = ZipOutputStrategy.create(path); + } else { + _outputStrategy = new FileOutputStrategy(path); + } + } + + public void excludeOptionalAndMissingFields(Class entityType, + Iterable entities) { + if (_excludeOptionalAndMissing == null) { + _excludeOptionalAndMissing = new ExcludeOptionalAndMissingEntitySchemaFactory( + _entitySchemaFactory); + } + _excludeOptionalAndMissing.scanEntities(entityType, entities); + } + + public void handleEntity(Object entity) { + Class entityType = entity.getClass(); + EntitySchemaFactory schemaFactory = _excludeOptionalAndMissing != null + ? _excludeOptionalAndMissing : _entitySchemaFactory; + IndividualCsvEntityWriter writer = _outputStrategy.getEntityWriter( + schemaFactory, _context, entityType); + writer.handleEntity(entity); + } + + public void flush() throws IOException { + _outputStrategy.flush(); + } + + public void close() throws IOException { + _outputStrategy.close(); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriterFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriterFactory.java new file mode 100644 index 000000000..2e9947b95 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvEntityWriterFactory.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.PrintWriter; +import java.io.Writer; + +import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +public class CsvEntityWriterFactory { + + private EntitySchemaFactory _entitySchemaFactory = new DefaultEntitySchemaFactory(); + + private CsvEntityContext _context = new CsvEntityContextImpl(); + + private TokenizerStrategy _tokenizerStrategy = new CsvTokenizerStrategy(); + + public EntitySchemaFactory getEntitySchemaFactory() { + return _entitySchemaFactory; + } + + public void setEntitySchemaFactory(EntitySchemaFactory entitySchemaFactory) { + _entitySchemaFactory = entitySchemaFactory; + } + + public CsvEntityContext getContext() { + return _context; + } + + public void setContext(CsvEntityContext context) { + _context = context; + } + + public void setTokenizerStrategy(TokenizerStrategy tokenizerStrategy) { + _tokenizerStrategy = tokenizerStrategy; + } + + public EntityHandler createWriter(Class entityType, Writer writer) { + EntitySchema schema = _entitySchemaFactory.getSchema(entityType); + IndividualCsvEntityWriter entityWriter = new IndividualCsvEntityWriter( + _context, schema, new PrintWriter(writer)); + if (_tokenizerStrategy != null) + entityWriter.setTokenizerStrategy(_tokenizerStrategy); + return entityWriter; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvInputSource.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvInputSource.java new file mode 100644 index 000000000..1f1a1f15e --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvInputSource.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.IOException; +import java.io.InputStream; + +public interface CsvInputSource { + public boolean hasResource(String name) throws IOException; + public InputStream getResource(String name) throws IOException; + public void close() throws IOException; +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvTokenizerStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvTokenizerStrategy.java new file mode 100644 index 000000000..708fc89b0 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/CsvTokenizerStrategy.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.List; + +public class CsvTokenizerStrategy implements TokenizerStrategy { + + private CSVLibrary _csv = new CSVLibrary(); + + public CSVLibrary getCsvParser() { + return _csv; + } + + public void setCsvParser(CSVLibrary csv) { + _csv = csv; + } + + @Override + public List parse(String line) { + return _csv.parse(line); + } + + @Override + public String format(Iterable tokens) { + return CSVLibrary.getIterableAsCSV(tokens); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimitedTextParser.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimitedTextParser.java new file mode 100644 index 000000000..95ab1e271 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimitedTextParser.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.ArrayList; +import java.util.List; + +/** + * Why do we have our own parser for CSV-like data when there are a couple of + * existing Java libraries? Mostly because we need to be able to handle some + * malformed CSV that most parsers would choke on but that agencies produce and + * google seems to validate as well. + * + * @author bdferris + * + */ +public class DelimitedTextParser { + + private enum EParseState { + TRIM_INIT_WHITESPACE, DATA, DATA_IN_QUOTES, END_QUOTE + } + + private final char _delimiter; + + private boolean _trimInitialWhitespace = false; + + public DelimitedTextParser(char delimiter) { + _delimiter = delimiter; + } + + public void setTrimInitialWhitespace(boolean trimInitialWhitespace) { + _trimInitialWhitespace = trimInitialWhitespace; + } + + public final List parse(String line) { + + StringBuilder token = new StringBuilder(); + List tokens = new ArrayList(); + if (line.length() > 0) + tokens.add(token); + + EParseState resetState = _trimInitialWhitespace + ? EParseState.TRIM_INIT_WHITESPACE : EParseState.DATA; + EParseState state = resetState; + + for (int i = 0; i < line.length(); i++) { + char c = line.charAt(i); + switch (state) { + case TRIM_INIT_WHITESPACE: + if (c == _delimiter) { + token = new StringBuilder(); + tokens.add(token); + } else { + switch (c) { + case ' ': + break; + case '"': + if (token.length() == 0) + state = EParseState.DATA_IN_QUOTES; + else + token.append(c); + break; + default: + state = EParseState.DATA; + token.append(c); + break; + } + } + break; + case DATA: + if (c == _delimiter) { + token = new StringBuilder(); + tokens.add(token); + state = resetState; + } else { + switch (c) { + case '"': + if (token.length() == 0) + state = EParseState.DATA_IN_QUOTES; + else + token.append(c); + break; + default: + token.append(c); + break; + } + } + break; + case DATA_IN_QUOTES: + switch (c) { + case '"': + state = EParseState.END_QUOTE; + break; + default: + token.append(c); + break; + } + break; + case END_QUOTE: + if (c == _delimiter) { + token = new StringBuilder(); + tokens.add(token); + state = resetState; + break; + } else { + switch (c) { + case '"': + token.append('"'); + state = EParseState.DATA_IN_QUOTES; + break; + default: + token.append(c); + state = EParseState.DATA; + break; + } + } + break; + } + } + List retro = new ArrayList(tokens.size()); + for (StringBuilder b : tokens) + retro.add(b.toString()); + return retro; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimiterTokenizerStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimiterTokenizerStrategy.java new file mode 100644 index 000000000..3b2b6e579 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/DelimiterTokenizerStrategy.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.Arrays; +import java.util.List; + +public class DelimiterTokenizerStrategy implements TokenizerStrategy { + + private final String _delimiter; + + private boolean _replaceLiteralNullValues = false; + + public DelimiterTokenizerStrategy(String delimiter) { + _delimiter = delimiter; + } + + /** + * This is a bit of a hack + * + * @param replaceLiteralNullValues + */ + public void setReplaceLiteralNullValues(boolean replaceLiteralNullValues) { + _replaceLiteralNullValues = replaceLiteralNullValues; + } + + @Override + public List parse(String line) { + String[] tokens = line.split(_delimiter); + if (_replaceLiteralNullValues) { + for (int i = 0; i < tokens.length; ++i) { + String value = tokens[i].toLowerCase(); + if (value.equals("null")) + tokens[i] = ""; + } + } + return Arrays.asList(tokens); + } + + @Override + public String format(Iterable tokens) { + StringBuilder b = new StringBuilder(); + boolean seenFirst = false; + for (String token : tokens) { + if (seenFirst) + b.append(_delimiter); + else + seenFirst = true; + b.append(token); + } + return b.toString(); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/EntityHandler.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/EntityHandler.java new file mode 100644 index 000000000..304e03ba7 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/EntityHandler.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +public interface EntityHandler { + public void handleEntity(Object bean); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileCsvInputSource.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileCsvInputSource.java new file mode 100644 index 000000000..74e1f72a9 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileCsvInputSource.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class FileCsvInputSource implements CsvInputSource { + + private File _sourceDirectory; + + public FileCsvInputSource(File sourceDirectory) { + _sourceDirectory = sourceDirectory; + } + + public boolean hasResource(String name) throws IOException { + File file = new File(_sourceDirectory, name); + return file.exists(); + } + + public InputStream getResource(String name) throws IOException { + File file = new File(_sourceDirectory, name); + return new FileInputStream(file); + } + + public void close() throws IOException { + + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileOutputStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileOutputStrategy.java new file mode 100644 index 000000000..e45544b00 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/FileOutputStrategy.java @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; + +import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +/** + * Implementation of {@link OutputStrategy} that supports writing entities to + * individual files within an output directory. + * + * @author bdferris + * + */ +class FileOutputStrategy implements OutputStrategy { + + private final File _outputDirectory; + + private Map, IndividualCsvEntityWriter> _writersByType = new HashMap, IndividualCsvEntityWriter>(); + + public FileOutputStrategy(File outputDirectory) { + _outputDirectory = outputDirectory; + } + + @Override + public IndividualCsvEntityWriter getEntityWriter( + EntitySchemaFactory entitySchemaFactory, CsvEntityContext context, + Class entityType) { + + IndividualCsvEntityWriter entityWriter = _writersByType.get(entityType); + if (entityWriter == null) { + EntitySchema schema = entitySchemaFactory.getSchema(entityType); + File outputFile = new File(_outputDirectory, schema.getFilename()); + + if (!_outputDirectory.exists()) + _outputDirectory.mkdirs(); + + PrintWriter writer = openOutput(outputFile, entityType); + entityWriter = new IndividualCsvEntityWriter(context, schema, writer); + _writersByType.put(entityType, entityWriter); + } + return entityWriter; + } + + @Override + public void flush() { + for (IndividualCsvEntityWriter writer : _writersByType.values()) + writer.flush(); + } + + @Override + public void close() throws IOException { + for (IndividualCsvEntityWriter writer : _writersByType.values()) + writer.close(); + } + + private PrintWriter openOutput(File outputFile, Class entityType) { + try { + return new PrintWriter(outputFile, "UTF-8"); + } catch (IOException ex) { + throw new CsvEntityIOException(entityType, outputFile.getAbsolutePath(), + 0, ex); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/HasExtensions.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/HasExtensions.java new file mode 100644 index 000000000..201000f5d --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/HasExtensions.java @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import org.onebusaway.csv_entities.schema.AbstractEntitySchemaFactoryImpl; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +/** + * Entity types that wish to support "extensions" should implement this type. + * + * Extensions allow users of a CSV-entity-based library to add custom fields to + * an existing entity type without modify the source of the base entity type. + * For example, consider a regular entity type named person: + * + *
{@code
+ * public class Person implements HasExtensions {
+ *   private int id;
+ *   private String name;
+ *   ...
+ * }
+ * }
+ * + * Now consider CSV data for such an entity with an additional custom field + * "age": + * + *
{@code
+ * id,name,age
+ * 1,Alice,27
+ * 2,Bob,45
+ * }
+ * + * Normally, to parse the age field, one would extend the Person + * entity. However, if the entity type is being distributed as part of a + * pre-compiled library and the user wants to extend it without modifying the + * base source code, they can use an extension type instead: + * + *
{@code
+ * public class PersonExtension {
+ *   private int age;
+ *   ...
+ * }
+ * }
+ * + * The extension type can be registered with your {@link EntitySchemaFactory} to + * indicate that the extension type should be used to process additional fields + * when reading or writing instances of the base entity type. + * + *
{@code
+ * DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory();
+ * factory.addExtension(Person.class, PersonExtension.class);
+ *   
+ * CsvEntityReader reader = new CsvEntityReader();
+ * reader.setEntitySchemaFactory(factory);
+ * }
+ * + * Now when instances of Person are read, instances of + * PersonExtension will be created and read for each + * Person as well. These extension instances can be accessed via + * the methods of {@link HasExtensions}: + * + *
{@code
+ * Person person = ...
+ * PersonExtension extension = person.getExtension(PersonExtension.class);
+ * }
+ * + * See {@link AbstractEntitySchemaFactoryImpl#addExtension(Class, Class)} for + * more details on registering specific extension types. + * + * @author bdferris + */ +public interface HasExtensions { + + /** + * Add an extension object of the specified type. + * + * @param type + * @param extension + */ + public void putExtension(Class type, Object extension); + + /** + * + * @param type + * @return an extension object of the specified type, or null if none has been + * registered. + */ + public X getExtension(Class type); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityReader.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityReader.java new file mode 100644 index 000000000..6ae2d5e95 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityReader.java @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + */ +package org.onebusaway.csv_entities; + +import org.onebusaway.csv_entities.exceptions.EntityInstantiationException; +import org.onebusaway.csv_entities.schema.BaseEntitySchema; +import org.onebusaway.csv_entities.schema.BeanWrapper; +import org.onebusaway.csv_entities.schema.BeanWrapperFactory; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.EntityValidator; +import org.onebusaway.csv_entities.schema.ExtensionEntitySchema; +import org.onebusaway.csv_entities.schema.FieldMapping; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class IndividualCsvEntityReader implements CSVListener { + + private static Logger _log = LoggerFactory.getLogger(IndividualCsvEntityReader.class); + + private EntityHandler _handler; + + private CsvEntityContext _context; + + private EntitySchema _schema; + + private boolean _initialized = false; + + private List _fields; + + private int _line = 1; + + private boolean _verbose = false; + + private boolean _trimValues = false; + + public IndividualCsvEntityReader(CsvEntityContext context, + EntitySchema schema, EntityHandler handler) { + _handler = handler; + _context = context; + _schema = schema; + + List inOrder = _schema.getFieldsInOrder(); + if (!inOrder.isEmpty()) { + _initialized = true; + _fields = inOrder; + } + } + + public IndividualCsvEntityReader(EntityHandler handler, + CsvEntityContext context, EntitySchema schema, List fields) { + this(context, schema, handler); + _initialized = true; + _fields = fields; + } + + public void setVerbose(boolean verbose) { + _verbose = verbose; + } + + public void setTrimValues(boolean trimValues) { + _trimValues = trimValues; + } + + public void handleLine(List line) throws Exception { + + if (line.size() == 0) + return; + + if (_trimValues) { + for (int i = 0; i < line.size(); i++) + line.set(i, line.get(i).trim()); + } + + if (!_initialized) { + readSchema(line); + _initialized = true; + } else { + readEntity(line); + } + _line++; + if (_verbose && _line % 1000 == 0) + System.out.println("entities=" + _line); + } + + private void readSchema(List line) { + _fields = line; + } + + private void readEntity(List line) { + + if (line.size() != _fields.size()) { + _log.warn("expected and actual number of csv fields differ: type=" + + _schema.getEntityClass().getName() + " line # " + _line + + " expected=" + _fields.size() + " actual=" + line.size()); + while (line.size() < _fields.size()) + line.add(""); + } + + Object object = createNewEntityInstance(_schema); + BeanWrapper wrapper = BeanWrapperFactory.wrap(object); + + Map values = new HashMap(); + + for (int i = 0; i < line.size(); i++) { + String csvFieldName = _fields.get(i); + String value = line.get(i); + values.put(csvFieldName, value); + } + + for (FieldMapping mapping : _schema.getFields()) + mapping.translateFromCSVToObject(_context, values, wrapper); + + if (object instanceof HasExtensions) { + HasExtensions hasExtensions = (HasExtensions) object; + for (ExtensionEntitySchema extensionSchema : _schema.getExtensions()) { + Object extension = createNewEntityInstance(extensionSchema); + BeanWrapper extensionWrapper = BeanWrapperFactory.wrap(extension); + for (FieldMapping mapping : extensionSchema.getFields()) { + mapping.translateFromCSVToObject(_context, values, extensionWrapper); + } + hasExtensions.putExtension(extensionSchema.getEntityClass(), extension); + } + } + + for (EntityValidator validator : _schema.getValidators()) + validator.validateEntity(_context, values, wrapper); + + _handler.handleEntity(object); + } + + private static Object createNewEntityInstance(BaseEntitySchema schema) { + Class entityClass = schema.getEntityClass(); + try { + return entityClass.newInstance(); + } catch (Exception ex) { + throw new EntityInstantiationException(entityClass, ex); + } + } +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java new file mode 100644 index 000000000..e6140156b --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + */ +package org.onebusaway.csv_entities; + +import org.onebusaway.csv_entities.schema.BeanWrapper; +import org.onebusaway.csv_entities.schema.BeanWrapperFactory; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.ExtensionEntitySchema; +import org.onebusaway.csv_entities.schema.FieldMapping; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class IndividualCsvEntityWriter implements EntityHandler { + + private PrintWriter _writer; + + private List _fieldNames = new ArrayList(); + + private EntitySchema _schema; + + private CsvEntityContext _context; + + private TokenizerStrategy _tokenizerStrategy = new CsvTokenizerStrategy(); + + private boolean _seenFirstRecord = false; + + public IndividualCsvEntityWriter(CsvEntityContext context, + EntitySchema schema, PrintWriter writer) { + _writer = writer; + _schema = schema; + _context = context; + + } + + public void setTokenizerStrategy(TokenizerStrategy tokenizerStrategy) { + _tokenizerStrategy = tokenizerStrategy; + } + + public void handleEntity(Object object) { + + if (!_seenFirstRecord) { + + _fieldNames.clear(); + for (FieldMapping field : _schema.getFields()) + field.getCSVFieldNames(_fieldNames); + + if (object instanceof HasExtensions) { + for (ExtensionEntitySchema extension : _schema.getExtensions()) { + for (FieldMapping field : extension.getFields()) { + field.getCSVFieldNames(_fieldNames); + } + } + } + + _writer.println(_tokenizerStrategy.format(_fieldNames)); + + _seenFirstRecord = true; + } + + BeanWrapper wrapper = BeanWrapperFactory.wrap(object); + Map csvValues = new HashMap(); + for (FieldMapping field : _schema.getFields()) { + field.translateFromObjectToCSV(_context, wrapper, csvValues); + } + if (object instanceof HasExtensions) { + HasExtensions hasExtensions = (HasExtensions) object; + for (ExtensionEntitySchema extensionSchema : _schema.getExtensions()) { + Object extension = hasExtensions.getExtension(extensionSchema.getEntityClass()); + if (extension != null) { + BeanWrapper extensionWrapper = BeanWrapperFactory.wrap(extension); + for (FieldMapping field : extensionSchema.getFields()) { + field.translateFromObjectToCSV(_context, extensionWrapper, + csvValues); + } + } + } + } + + List values = new ArrayList(csvValues.size()); + for (String fieldName : _fieldNames) { + Object value = csvValues.get(fieldName); + if (value == null) + value = ""; + values.add(value.toString()); + } + String line = _tokenizerStrategy.format(values); + _writer.println(line); + } + + public void flush() { + _writer.flush(); + } + + public void close() { + _writer.close(); + } + +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ListEntityHandler.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ListEntityHandler.java new file mode 100644 index 000000000..906775e10 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ListEntityHandler.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ListEntityHandler implements EntityHandler, Iterable { + + private List _values = new ArrayList(); + + public List getValues() { + return _values; + } + + @Override + public Iterator iterator() { + return _values.iterator(); + } + + /**** + * {@link EntityHandler} Interface + ****/ + + @SuppressWarnings("unchecked") + @Override + public void handleEntity(Object bean) { + _values.add((T) bean); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/OutputStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/OutputStrategy.java new file mode 100644 index 000000000..1df7fd3ae --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/OutputStrategy.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.IOException; + +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +/** + * Generic strategy interface for creating {@link IndividualCsvEntityWriter} + * writers for outputting CSV entities. + * + * @author bdferris + * + */ +interface OutputStrategy { + + public IndividualCsvEntityWriter getEntityWriter( + EntitySchemaFactory entitySchemaFactory, CsvEntityContext context, + Class entityType); + + public void flush() throws IOException; + + public void close() throws IOException; +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/TokenizerStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/TokenizerStrategy.java new file mode 100644 index 000000000..28a9bf97a --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/TokenizerStrategy.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.List; + +public interface TokenizerStrategy { + public List parse(String line); + public String format(Iterable tokens); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipFileCsvInputSource.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipFileCsvInputSource.java new file mode 100644 index 000000000..1bc5cec8b --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipFileCsvInputSource.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class ZipFileCsvInputSource implements CsvInputSource { + + private ZipFile _zipFile; + + public ZipFileCsvInputSource(ZipFile zipFile) { + _zipFile = zipFile; + } + + public boolean hasResource(String name) throws IOException { + ZipEntry entry = _zipFile.getEntry(name); + return entry != null; + } + + public InputStream getResource(String name) throws IOException { + ZipEntry entry = _zipFile.getEntry(name); + return _zipFile.getInputStream(entry); + } + + public void close() throws IOException { + _zipFile.close(); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipOutputStrategy.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipOutputStrategy.java new file mode 100644 index 000000000..fbc98d428 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/ZipOutputStrategy.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.onebusaway.csv_entities.exceptions.CsvException; +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.EntitySchemaFactory; + +/** + * Implementation of {@link OutputStrategy} that supports writing entities to + * entries within a Zip file. All entities of a particular type must be + * serialized at the same time. That is to say, writing a few entities of type + * A, then type B, and then type A again is not supported, since the Java + * {@link ZipOutputStream} does not support random-access output to different + * zip entries. + * + * @author bdferris + * + */ +class ZipOutputStrategy implements OutputStrategy { + + private final ZipOutputStream _out; + + private final PrintWriter _writer; + + private final Set> _typesWeHaveAlreadySeen = new HashSet>(); + + private Class _currentType = null; + + private IndividualCsvEntityWriter _currentWriter = null; + + public ZipOutputStrategy(ZipOutputStream out, PrintWriter writer) { + _out = out; + _writer = writer; + } + + public static ZipOutputStrategy create(File path) { + try { + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(path)); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8")); + return new ZipOutputStrategy(out, writer); + } catch (IOException ex) { + throw new CsvException("Error creating ZipOutputStrategy for path " + + path, ex); + } + } + + @Override + public IndividualCsvEntityWriter getEntityWriter( + EntitySchemaFactory entitySchemaFactory, CsvEntityContext context, + Class entityType) { + + if (_currentType != null && _currentType.equals(entityType)) { + return _currentWriter; + } + closeCurrentEntityWriter(); + if (!_typesWeHaveAlreadySeen.add(entityType)) { + throw new IllegalStateException( + "When writing to a ZIP output feed, entities cannot be written in arbitrary order " + + "but must be grouped by type. You have attempted to write an entity of type " + + entityType + + " but the zip entry for that type has already been closed."); + } + + _currentType = entityType; + EntitySchema schema = entitySchemaFactory.getSchema(entityType); + ZipEntry entry = new ZipEntry(schema.getFilename()); + try { + _out.putNextEntry(entry); + } catch (IOException ex) { + throw new CsvException("Error opening zip entry", ex); + } + + _currentWriter = new IndividualCsvEntityWriter(context, schema, _writer); + return _currentWriter; + } + + @Override + public void flush() throws IOException { + _out.flush(); + } + + @Override + public void close() throws IOException { + closeCurrentEntityWriter(); + _out.close(); + } + + private void closeCurrentEntityWriter() { + if (_currentType != null) { + try { + _writer.flush(); + _out.closeEntry(); + } catch (IOException ex) { + throw new CsvException("Error closing zip entry", ex); + } + _currentWriter = null; + _currentType = null; + } + } + +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityException.java new file mode 100644 index 000000000..947507d3f --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityException.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Extend from {@link Exception} or {@link RuntimeException}? The debate rages + * on, but I chose to extend from {@link RuntimeException} to maintain + * compatibility with existing method signatures and because most of the + * exceptions thrown here are non-recoverable. That is, you typically just log + * them and exit. + * + * @author bdferris + */ +public abstract class CsvEntityException extends CsvException { + + private static final long serialVersionUID = 1L; + + private final Class _entityType; + + public CsvEntityException(Class entityType, String message) { + super(message); + _entityType = entityType; + } + + public CsvEntityException(Class entityType, String message, Throwable cause) { + super(message, cause); + _entityType = entityType; + } + + public CsvEntityException(Class entityType, Throwable cause) { + super(cause); + _entityType = entityType; + } + + public Class getEntityType() { + return _entityType; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityIOException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityIOException.java new file mode 100644 index 000000000..ea2af61fd --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvEntityIOException.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Indicates an error was thrown when reading / writing CSV. This exception + * provides information about line number and path information about where the + * error occurred. + * + * @author bdferris + * + */ +public class CsvEntityIOException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + private String _path; + + private int _lineNumber; + + public CsvEntityIOException(Class entityType, String path, int lineNumber, + Throwable cause) { + super(entityType, "io error: entityType=" + entityType.getName() + " path=" + + path + " lineNumber=" + lineNumber, cause); + _path = path; + _lineNumber = lineNumber; + } + + public String getPath() { + return _path; + } + + public int getLineNumber() { + return _lineNumber; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvException.java new file mode 100644 index 000000000..dc97786ab --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/CsvException.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Extend from {@link Exception} or {@link RuntimeException}? The debate rages + * on, but I chose to extend from {@link RuntimeException} to maintain + * compatibility with existing method signatures and because most of the + * exceptions thrown here are non-recoverable. That is, you typically just log + * them and exit. + * + * @author bdferris + */ +public class CsvException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public CsvException(String message) { + super(message); + } + + public CsvException(String message, Throwable cause) { + super(message, cause); + } + + public CsvException(Throwable cause) { + super(cause); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/EntityInstantiationException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/EntityInstantiationException.java new file mode 100644 index 000000000..e09137870 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/EntityInstantiationException.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Indicates an error when attempting to instantiate an instance of the + * specified entity type + * + * @author bdferris + */ +public class EntityInstantiationException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public EntityInstantiationException(Class entityType, Throwable cause) { + super(entityType, "error instantiating entity of type=" + + entityType.getName(), cause); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/IntrospectionException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/IntrospectionException.java new file mode 100644 index 000000000..c3404fa22 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/IntrospectionException.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import java.beans.Introspector; + +/** + * Indicates that introspection failed for the specified entity type. Usually + * indicates a failure with {@link Introspector#getBeanInfo(Class)}. + * + * @author bdferris + * @see Introspector#getBeanInfo(Class) + */ +public class IntrospectionException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public IntrospectionException(Class entityType) { + super(entityType, "introspection error for type " + entityType); + } + + public IntrospectionException(Class entityType, Throwable cause) { + super(entityType, "introspection error for type " + entityType,cause); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/InvalidValueEntityException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/InvalidValueEntityException.java new file mode 100644 index 000000000..e3985a0f8 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/InvalidValueEntityException.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Indicates that the value of the specified field for the specified entity type + * is invalid. + * + * @author bdferris + */ +public class InvalidValueEntityException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + private final String _fieldName; + + private final String _fieldValue; + + public InvalidValueEntityException(Class entityType, String fieldName, + String fieldValue) { + super(entityType, "invalid value \"" + fieldValue + "\" for field \"" + + fieldName + "\""); + _fieldName = fieldName; + _fieldValue = fieldValue; + } + + public String getFieldName() { + return _fieldName; + } + + public String getFieldValue() { + return _fieldValue; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MethodInvocationException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MethodInvocationException.java new file mode 100644 index 000000000..30215a411 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MethodInvocationException.java @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import java.lang.reflect.Method; + +/** + * Indicates an error when attempting to invoke the specified method on an + * instance of the specified entity class + * + * @author bdferris + */ +public class MethodInvocationException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public MethodInvocationException(Class entityType, Method method, + Exception ex) { + super(entityType, "error invoking method " + method + " for entityType " + + entityType.getName(), ex); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredEntityException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredEntityException.java new file mode 100644 index 000000000..9b3f7220f --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredEntityException.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import org.onebusaway.csv_entities.schema.EntitySchema; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; + +/** + * Indicates that the specified entity type is marked as required, but no input + * file or source was found for that entity. + * + * @author bdferris + * @see EntitySchema#isRequired() + * @see CsvFields#required() + * @see CsvEntityMappingBean#isRequired() + */ +public class MissingRequiredEntityException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + private String _fileName; + + public MissingRequiredEntityException(Class entityType, String fileName) { + super(entityType, "missing required entity: type=" + entityType.getName() + + " filename=" + fileName); + _fileName = fileName; + } + + public String getFileName() { + return _fileName; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredFieldException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredFieldException.java new file mode 100644 index 000000000..cfe1e3223 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/MissingRequiredFieldException.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.beans.CsvFieldMappingBean; + +/** + * Indiciates that the specified field for the specified entity type is marked + * as required, but that no value was included in either the CSV source (just an + * empty value) or the entity object (null value). + * + * @author bdferris + * @see CsvField#optional() + * @see CsvFieldMappingBean#isOptional() + */ +public class MissingRequiredFieldException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + private String _fieldName; + + public MissingRequiredFieldException(Class entityType, String fieldName) { + super(entityType, "missing required field: " + fieldName); + _fieldName = fieldName; + } + + public String getFieldName() { + return _fieldName; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoCsvFieldsAnnotationException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoCsvFieldsAnnotationException.java new file mode 100644 index 000000000..596b62fa9 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoCsvFieldsAnnotationException.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import org.onebusaway.csv_entities.schema.AnnotationDrivenEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; + +/** + * Indicates that an entity type was passed to + * {@link AnnotationDrivenEntitySchemaFactory} for introspection and the + * specified entity type did not have a {@link CsvFields} class annotation + * + * @author bdferris + * @see CsvFields + * @see AnnotationDrivenEntitySchemaFactory + */ +public class NoCsvFieldsAnnotationException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public NoCsvFieldsAnnotationException(Class entityType) { + super(entityType, "No @CsvFields annotation found for entity type " + + entityType.getName()); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoDefaultConverterException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoDefaultConverterException.java new file mode 100644 index 000000000..9a1a0a0eb --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoDefaultConverterException.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +import org.apache.commons.beanutils.ConvertUtils; + +/** + * Error indicating that no default converter could be found for converting CSV + * string data into the specified type for the target entity's specified field. + * We use the {@link ConvertUtils#lookup(Class)} method to find a converter. + * + * @author bdferris + * @see ConvertUtils + */ +public class NoDefaultConverterException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + private final String _csvFieldName; + private final String _objFieldName; + private final Class _objFieldType; + + public NoDefaultConverterException(Class entityType, String csvFieldName, + String objFieldName, Class objFieldType) { + super(entityType, "no default converter found: entityType=" + + entityType.getName() + " csvField=" + csvFieldName + " objField=" + + objFieldName + " objType=" + objFieldType); + _csvFieldName = csvFieldName; + _objFieldName = objFieldName; + _objFieldType = objFieldType; + } + + public String getCsvFieldName() { + return _csvFieldName; + } + + public String getObjFieldName() { + return _objFieldName; + } + + public Class getObjFieldType() { + return _objFieldType; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoSuchPropertyException.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoSuchPropertyException.java new file mode 100644 index 000000000..6e5ce343e --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/exceptions/NoSuchPropertyException.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.exceptions; + +/** + * Indicates that the specified entity type does not have a property with the + * given name, or that there was an error examining the property. + * + * @author bdferris + */ +public class NoSuchPropertyException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + private String _propertyName; + + public NoSuchPropertyException(Class entityType, String propertyName) { + super(entityType, "no such property \"" + propertyName + "\" for type " + + entityType.getName()); + _propertyName = propertyName; + } + + public NoSuchPropertyException(Class entityType, String propertyName, + Exception ex) { + super(entityType, "no such property \"" + propertyName + "\" for type " + + entityType.getName(), ex); + _propertyName = propertyName; + } + + public String getPropertyName() { + return _propertyName; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImpl.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImpl.java new file mode 100644 index 000000000..cf9582669 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImpl.java @@ -0,0 +1,486 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.onebusaway.csv_entities.HasExtensions; +import org.onebusaway.csv_entities.exceptions.EntityInstantiationException; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFieldNameConvention; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; +import org.onebusaway.csv_entities.schema.beans.CsvFieldMappingBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractEntitySchemaFactoryImpl implements + EntitySchemaFactory, ListableCsvMappingFactory { + + private static final Logger _log = LoggerFactory.getLogger(AbstractEntitySchemaFactoryImpl.class); + + private boolean _initialized = false; + + private Map, CsvEntityMappingBean> _mappingBeansByClass = new HashMap, CsvEntityMappingBean>(); + + private Map, List>> _extensionsByClass = new HashMap, List>>(); + + private Map, EntitySchema> _schemasByClass = new HashMap, EntitySchema>(); + + /** + * It can be useful to support the reading and writing of additional custom + * fields for a particular entity type without the need to modify the base + * entity type directly. We support this capability through "extensions": + * additional extension entity types, defining their own custom fields, that + * are associated with a base entity type and then processed along with the + * base entity when reading and writing data. Every time a base entity is + * read, an extension entity is also read, and associated with the base entity + * via the {@link HasExtensions} interface, which the base entity must + * implement in order to support extensions. + * + * @param type the base schema entity type to extend + * @param extensionType the extension type, with additional fields to read and + * write + */ + public void addExtension(Class type, + Class extensionType) { + List> extensionTypes = _extensionsByClass.get(type); + if (extensionTypes == null) { + extensionTypes = new ArrayList>(); + _extensionsByClass.put(type, extensionTypes); + } + extensionTypes.add(extensionType); + + _schemasByClass.remove(type); + } + + /**** + * {@link ListableCsvMappingFactory} Interface + ****/ + + public Collection getEntityMappings() { + initialize(); + return new ArrayList(_mappingBeansByClass.values()); + } + + /**** + * {@link EntitySchemaFactory} Interface + ****/ + + public EntitySchema getSchema(Class entityClass) { + + initialize(); + + EntitySchema schema = _schemasByClass.get(entityClass); + + if (schema == null) { + schema = createSchemaForEntityClass(entityClass); + _schemasByClass.put(entityClass, schema); + } + + return schema; + } + + /**** + * Protected Methods + ****/ + + protected abstract void processBeanDefinitions(); + + protected void registerBeanDefinition(CsvEntityMappingBean bean) { + CsvEntityMappingBean existingBean = _mappingBeansByClass.get(bean.getType()); + if (existingBean != null) { + CsvEntityMappingBean merged = new CsvEntityMappingBean(bean.getType()); + mergeBeans(existingBean, merged); + mergeBeans(bean, merged); + bean = merged; + } + _mappingBeansByClass.put(bean.getType(), bean); + } + + protected void applyCsvFieldsAnnotationToBean(Class entityClass, + CsvEntityMappingBean entityBean) { + + CsvFields csvFields = entityClass.getAnnotation(CsvFields.class); + + if (csvFields != null) { + entityBean.setFilename(csvFields.filename()); + if (!csvFields.prefix().equals("")) + entityBean.setPrefix(csvFields.prefix()); + if (csvFields.required()) + entityBean.setRequired(csvFields.required()); + String[] fieldsInOrder = csvFields.fieldOrder(); + if (fieldsInOrder.length != 0) { + for (String fieldInOrder : fieldsInOrder) + entityBean.addFieldInOrder(fieldInOrder); + } + if (csvFields.fieldNameConvention() != CsvFieldNameConvention.UNSPECIFIED) { + entityBean.setFieldNameConvention(csvFields.fieldNameConvention()); + } + } + } + + protected void applyCsvFieldAnnotationToBean(Field field, + CsvFieldMappingBean fieldBean) { + CsvField csvField = field.getAnnotation(CsvField.class); + + if (csvField != null) { + if (!csvField.name().equals("")) + fieldBean.setName(csvField.name()); + if (csvField.ignore()) + fieldBean.setIgnore(csvField.ignore()); + if (csvField.optional()) + fieldBean.setOptional(csvField.optional()); + if (csvField.alwaysIncludeInOutput()) { + fieldBean.setAlwaysIncludeInOutput(csvField.alwaysIncludeInOutput()); + } + if (csvField.order() != Integer.MAX_VALUE) + fieldBean.setOrder(csvField.order()); + if (!csvField.defaultValue().isEmpty()) { + fieldBean.setDefaultValue(csvField.defaultValue()); + } + + Class mapping = csvField.mapping(); + if (!mapping.equals(FieldMappingFactory.class)) { + try { + FieldMappingFactory factory = mapping.newInstance(); + fieldBean.setMapping(factory); + } catch (Exception ex) { + throw new EntityInstantiationException(mapping, ex); + } + } + } + } + + /**** + * Private Methods + ****/ + + private void initialize() { + if (!_initialized) { + processBeanDefinitions(); + _initialized = true; + } + } + + private void mergeBeans(CsvEntityMappingBean source, + CsvEntityMappingBean target) { + if (source.isFilenameSet()) + target.setFilename(source.getFilename()); + if (source.isPrefixSet()) + target.setPrefix(source.getPrefix()); + if (source.isRequiredSet()) + target.setRequired(source.isRequired()); + if (source.isAutoGenerateSchemaSet()) + target.setAutoGenerateSchema(source.isAutoGenerateSchema()); + + List fieldsInOrder = source.getFieldsInOrder(); + if (!fieldsInOrder.isEmpty()) + target.setFieldsInOrder(fieldsInOrder); + + for (FieldMapping mapping : source.getAdditionalFieldMappings()) + target.addAdditionalFieldMapping(mapping); + + Map sourceFields = source.getFields(); + Map targetFields = target.getFields(); + for (Map.Entry entry : sourceFields.entrySet()) { + Field sourceField = entry.getKey(); + CsvFieldMappingBean sourceFieldBean = entry.getValue(); + CsvFieldMappingBean targetFieldBean = targetFields.get(sourceField); + if (targetFieldBean == null) + targetFieldBean = sourceFieldBean; + else + mergeFields(sourceFieldBean, targetFieldBean); + targetFields.put(sourceField, targetFieldBean); + } + } + + private void mergeFields(CsvFieldMappingBean source, + CsvFieldMappingBean target) { + if (source.isNameSet()) + target.setName(source.getName()); + if (source.isIgnoreSet()) + target.setIgnore(target.isIgnore()); + if (source.isMappingSet()) + target.setMapping(source.getMapping()); + if (source.isOptionalSet()) + target.setOptional(source.isOptional()); + if (source.isAlwaysIncludeInOutput()) { + target.setAlwaysIncludeInOutput(source.isAlwaysIncludeInOutput()); + } + if (source.isOrderSet()) + target.setOrder(source.getOrder()); + if (source.getDefaultValue() != null) { + target.setDefaultValue(source.getDefaultValue()); + } + } + + private EntitySchema createSchemaForEntityClass(Class entityClass) { + CsvEntityMappingBean mappingBean = getMappingBeanForEntityType(entityClass); + + String name = getEntityClassAsEntityName(entityClass); + if (mappingBean.isFilenameSet()) + name = mappingBean.getFilename(); + + boolean required = false; + if (mappingBean.isRequiredSet()) + required = mappingBean.isRequired(); + + EntitySchema schema = new EntitySchema(entityClass, name, required); + + fillSchemaForEntityClass(entityClass, mappingBean, schema); + + List fieldsInOrder = mappingBean.getFieldsInOrder(); + if (!fieldsInOrder.isEmpty()) + schema.setFieldsInOrder(fieldsInOrder); + + List> extensionTypes = _extensionsByClass.get(entityClass); + if (extensionTypes != null) { + for (Class extensionType : extensionTypes) { + CsvEntityMappingBean extensionMappingBean = getMappingBeanForEntityType(extensionType); + ExtensionEntitySchema extensionSchema = new ExtensionEntitySchema( + extensionType); + fillSchemaForEntityClass(extensionType, extensionMappingBean, + extensionSchema); + schema.addExtension(extensionSchema); + } + } + + return schema; + } + + private CsvEntityMappingBean getMappingBeanForEntityType(Class entityClass) { + CsvEntityMappingBean mappingBean = _mappingBeansByClass.get(entityClass); + if (mappingBean == null) { + mappingBean = new CsvEntityMappingBean(entityClass); + applyCsvFieldsAnnotationToBean(entityClass, mappingBean); + } + return mappingBean; + } + + private void fillSchemaForEntityClass(Class entityClass, + CsvEntityMappingBean mappingBean, BaseEntitySchema schema) { + Map existingFieldBeans = mappingBean.getFields(); + List fieldMappings = new ArrayList(); + + String prefix = ""; + if (mappingBean.isPrefixSet()) + prefix = mappingBean.getPrefix(); + + CsvFieldNameConvention fieldNameConvention = CsvFieldNameConvention.UNSPECIFIED; + if (mappingBean.getFieldNameConvention() != null) + fieldNameConvention = mappingBean.getFieldNameConvention(); + if (fieldNameConvention == CsvFieldNameConvention.UNSPECIFIED) + fieldNameConvention = CsvFieldNameConvention.UNDERSCORE; + + boolean autoGenerateSchema = true; + if (mappingBean.isAutoGenerateSchemaSet()) + autoGenerateSchema = mappingBean.isAutoGenerateSchema(); + + if (autoGenerateSchema) { + Set remainingFields = new LinkedHashSet(); + for (Field field : entityClass.getDeclaredFields()) { + remainingFields.add(field); + } + // We add known fields first so that we can maintain field order. + for (Map.Entry entry : existingFieldBeans.entrySet()) { + Field field = entry.getKey(); + if (!remainingFields.remove(field)) { + _log.warn("field found in mapping but not in class: " + field); + continue; + } + addFieldMapping(entityClass, prefix, fieldNameConvention, field, + entry.getValue(), fieldMappings); + } + // We add any remaining fields next. + for (Field field : remainingFields) { + CsvFieldMappingBean fieldMappingBean = new CsvFieldMappingBean(field); + applyCsvFieldAnnotationToBean(field, fieldMappingBean); + + // Ignore static or final fields + boolean ignore = (field.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) != 0; + if (ignore) + fieldMappingBean.setIgnore(ignore); + + addFieldMapping(entityClass, prefix, fieldNameConvention, field, + fieldMappingBean, fieldMappings); + } + } + + for (FieldMapping fieldMapping : mappingBean.getAdditionalFieldMappings()) + fieldMappings.add(fieldMapping); + + List sortableMappings = new ArrayList(); + List unsortableMappings = new ArrayList(); + for (FieldMapping fieldMapping : fieldMappings) { + if (fieldMapping.getOrder() == Integer.MAX_VALUE) { + unsortableMappings.add(fieldMapping); + } else { + sortableMappings.add(fieldMapping); + } + } + if (!sortableMappings.isEmpty()) { + Collections.sort(sortableMappings, new FieldMappingComparator()); + fieldMappings.clear(); + fieldMappings.addAll(sortableMappings); + fieldMappings.addAll(unsortableMappings); + } + + for (FieldMapping mapping : fieldMappings) + schema.addField(mapping); + + List validators = new ArrayList(); + validators.addAll(mappingBean.getValidators()); + + Collections.sort(validators, new ValidatorComparator()); + + for (EntityValidator validator : validators) + schema.addValidator(validator); + } + + private void addFieldMapping(Class entityClass, String prefix, + CsvFieldNameConvention fieldNameConvention, Field field, + CsvFieldMappingBean fieldMappingBean, List fieldMappings) { + if (fieldMappingBean.isIgnoreSet() && fieldMappingBean.isIgnore()) + return; + FieldMapping mapping = getFieldMapping(entityClass, field, + fieldMappingBean, prefix, fieldNameConvention); + fieldMappings.add(mapping); + } + + private FieldMapping getFieldMapping(Class entityClass, Field field, + CsvFieldMappingBean fieldMappingBean, String prefix, + CsvFieldNameConvention fieldNameConvention) { + + FieldMapping mapping = null; + + String objFieldName = field.getName(); + Class objFieldType = field.getType(); + + String csvFieldName = prefix + + getObjectFieldNameAsCSVFieldName(objFieldName, fieldNameConvention); + boolean required = true; + + if (fieldMappingBean.isOptionalSet()) + required = !fieldMappingBean.isOptional(); + + if (fieldMappingBean.isNameSet()) + csvFieldName = fieldMappingBean.getName(); + + if (fieldMappingBean.isMappingSet()) { + FieldMappingFactory factory = fieldMappingBean.getMapping(); + mapping = factory.createFieldMapping(this, entityClass, csvFieldName, + objFieldName, objFieldType, required); + } + + if (mapping == null) { + DefaultFieldMapping m = new DefaultFieldMapping(entityClass, + csvFieldName, objFieldName, objFieldType, required); + + mapping = m; + } + + if (mapping instanceof AbstractFieldMapping) { + AbstractFieldMapping fm = (AbstractFieldMapping) mapping; + if (fieldMappingBean.isOrderSet()) + fm.setOrder(fieldMappingBean.getOrder()); + if (fieldMappingBean.isAlwaysIncludeInOutputSet()) { + fm.setAlwaysIncludeInOutput(fieldMappingBean.isAlwaysIncludeInOutput()); + } + if (fieldMappingBean.getDefaultValue() != null) { + fm.setDefaultValue(fieldMappingBean.getDefaultValue()); + } + + try { + String name = field.getName(); + String isFieldSet = "is" + Character.toUpperCase(name.charAt(0)) + + name.substring(1) + "Set"; + + Method method = entityClass.getMethod(isFieldSet); + if (method != null + && (method.getReturnType() == Boolean.class || method.getReturnType() == Boolean.TYPE)) { + fm.setIsSetMethod(method); + } + } catch (Exception ex) { + // We ignore this + } + + } + + return mapping; + } + + private String getEntityClassAsEntityName(Class entityClass) { + String name = entityClass.getName(); + int index = name.lastIndexOf("."); + if (index != -1) + name = name.substring(index + 1); + return name; + } + + private String getObjectFieldNameAsCSVFieldName(String fieldName, + CsvFieldNameConvention fieldNameConvention) { + + if (fieldNameConvention == CsvFieldNameConvention.CAMEL_CASE) + return fieldName; + + if (fieldNameConvention == CsvFieldNameConvention.CAPITALIZED_CAMEL_CASE) { + return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + + StringBuilder b = new StringBuilder(); + boolean wasUpperCase = false; + + for (int i = 0; i < fieldName.length(); i++) { + char c = fieldName.charAt(i); + boolean isUpperCase = Character.isUpperCase(c); + if (isUpperCase) + c = Character.toLowerCase(c); + if (isUpperCase && !wasUpperCase) + b.append('_'); + b.append(c); + wasUpperCase = isUpperCase; + } + + return b.toString(); + } + + private static class FieldMappingComparator implements + Comparator { + public int compare(FieldMapping o1, FieldMapping o2) { + return o1.getOrder() - o2.getOrder(); + } + } + + private static class ValidatorComparator implements + Comparator { + public int compare(EntityValidator o1, EntityValidator o2) { + return o1.getOrder() - o2.getOrder(); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntityValidator.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntityValidator.java new file mode 100644 index 000000000..ca2f70845 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractEntityValidator.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; + +public class AbstractEntityValidator implements EntityValidator { + + private int _order = 0; + + public int getOrder() { + return _order; + } + + public void setOrder(int order) { + _order = order; + } + + public void validateCSV(CsvEntityContext context, BeanWrapper object, Map csvValues) { + + } + + public void validateEntity(CsvEntityContext context, Map csvValues, BeanWrapper object) { + + } + +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractFieldMapping.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractFieldMapping.java new file mode 100644 index 000000000..da3726310 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AbstractFieldMapping.java @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.onebusaway.csv_entities.exceptions.MethodInvocationException; +import org.onebusaway.csv_entities.exceptions.MissingRequiredFieldException; + +public abstract class AbstractFieldMapping implements SingleFieldMapping { + + protected final Class _entityType; + + protected final String _csvFieldName; + + protected final String _objFieldName; + + protected final boolean _required; + + protected int _order = Integer.MAX_VALUE; + + protected boolean _alwaysIncludeInOutput = false; + + protected String _defaultValue = null; + + protected Method _isSetMethod = null; + + public AbstractFieldMapping(Class entityType, String csvFieldName, + String objFieldName, boolean required) { + _entityType = entityType; + _csvFieldName = csvFieldName; + _objFieldName = objFieldName; + _required = required; + } + + public void setOrder(int order) { + _order = order; + } + + public void setDefaultValue(String defaultValue) { + _defaultValue = defaultValue; + } + + public void setAlwaysIncludeInOutput(boolean alwaysIncludeInOutput) { + _alwaysIncludeInOutput = alwaysIncludeInOutput; + } + + public void setIsSetMethod(Method isSetMethod) { + _isSetMethod = isSetMethod; + } + + /**** + * {@link SingleFieldMapping} + ****/ + + public String getCsvFieldName() { + return _csvFieldName; + } + + public String getObjFieldName() { + return _objFieldName; + } + + /*** + * {@link FieldMapping} Interface + ****/ + + @Override + public void getCSVFieldNames(Collection names) { + names.add(_csvFieldName); + } + + @Override + public int getOrder() { + return _order; + } + + @Override + public boolean isMissingAndOptional(Map csvValues) { + + boolean missing = isMissing(csvValues); + + if (_required && missing) + throw new MissingRequiredFieldException(_entityType, _csvFieldName); + + return missing; + } + + @Override + public boolean isMissingAndOptional(BeanWrapper object) { + boolean missing = isMissing(object); + + if (_required && missing) + throw new MissingRequiredFieldException(_entityType, _objFieldName); + + return missing; + } + + @Override + public boolean isAlwaysIncludeInOutput() { + return _alwaysIncludeInOutput; + } + + /**** + * Protected Methods + ****/ + + protected boolean isMissing(Map csvValues) { + return isMissing(csvValues, _csvFieldName); + } + + protected static boolean isMissing(Map csvValues, + String csvFieldName) { + return !(csvValues.containsKey(csvFieldName) && csvValues.get(csvFieldName).toString().length() > 0); + } + + protected boolean isMissing(BeanWrapper object) { + if (_isSetMethod != null) { + Object instance = object.getWrappedInstance(Object.class); + try { + Object r = _isSetMethod.invoke(instance); + if (r != null && r instanceof Boolean) { + Boolean b = (Boolean) r; + return !b.booleanValue(); + } + } catch (Exception ex) { + throw new MethodInvocationException(_entityType, _isSetMethod, ex); + } + } else { + Object obj = object.getPropertyValue(_objFieldName); + if (obj == null) { + return true; + } + if (_defaultValue != null && !_defaultValue.isEmpty()) { + return _defaultValue.equals(obj.toString()); + } + return (obj instanceof String && obj.toString().isEmpty()); + } + return false; + } + + protected boolean isOptional() { + return !_required; + } + +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AnnotationDrivenEntitySchemaFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AnnotationDrivenEntitySchemaFactory.java new file mode 100644 index 000000000..7d4bf4f02 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/AnnotationDrivenEntitySchemaFactory.java @@ -0,0 +1,303 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import org.onebusaway.csv_entities.exceptions.NoCsvFieldsAnnotationException; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; +import org.onebusaway.csv_entities.schema.beans.CsvFieldMappingBean; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class AnnotationDrivenEntitySchemaFactory extends AbstractEntitySchemaFactoryImpl { + + /** URL prefix for loading from the file system: "file:" */ + private static final String FILE_URL_PREFIX = "file:"; + + /** URL protocol for a file in the file system: "file" */ + private static final String URL_PROTOCOL_FILE = "file"; + + /** URL protocol for an entry from a jar file: "jar" */ + private static final String URL_PROTOCOL_JAR = "jar"; + + /** URL protocol for an entry from a zip file: "zip" */ + private static final String URL_PROTOCOL_ZIP = "zip"; + + /** URL protocol for an entry from a WebSphere jar file: "wsjar" */ + private static final String URL_PROTOCOL_WSJAR = "wsjar"; + + /** URL protocol for an entry from an OC4J jar file: "code-source" */ + private static final String URL_PROTOCOL_CODE_SOURCE = "code-source"; + + /** Separator between JAR URL and file path within the JAR */ + private static final String JAR_URL_SEPARATOR = "!/"; + + private final Logger _log = LoggerFactory.getLogger(AnnotationDrivenEntitySchemaFactory.class); + + private List _packagesToScan = new ArrayList(); + + private List> _classesToScan = new ArrayList>(); + + public void addPackageToScan(String packageToScan) { + _packagesToScan.add(packageToScan); + } + + public void addEntityClass(Class classToScan) { + _classesToScan.add(classToScan); + } + + @Override + protected void processBeanDefinitions() { + + for (Class entityClass : _classesToScan) { + CsvEntityMappingBean bean = getEntityMappingBeanForEntityClass(entityClass); + registerBeanDefinition(bean); + } + + try { + scanPackages(); + } catch (IOException ex) { + _log.warn("error scanning classpath for classes", ex); + } + } + + /**** + * Private Methods + ****/ + + private void go(String cName) { + try { + Class entityClass = Class.forName(cName); + CsvFields csvFields = entityClass.getAnnotation(CsvFields.class); + if (csvFields != null) { + CsvEntityMappingBean mappingBean = getEntityMappingBeanForEntityClass(entityClass); + registerBeanDefinition(mappingBean); + } + } catch (ClassNotFoundException ex) { + + } + } + + private CsvEntityMappingBean getEntityMappingBeanForEntityClass(Class entityClass) { + + CsvFields csvFields = entityClass.getAnnotation(CsvFields.class); + + if (csvFields == null) + throw new NoCsvFieldsAnnotationException(entityClass); + + CsvEntityMappingBean bean = new CsvEntityMappingBean(entityClass); + applyCsvFieldsAnnotationToBean(entityClass, bean); + + for (Field field : entityClass.getDeclaredFields()) { + + // Skip static final fields + if ((field.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) != 0) + continue; + + CsvFieldMappingBean fieldBean = new CsvFieldMappingBean(field); + applyCsvFieldAnnotationToBean(field, fieldBean); + + bean.addField(fieldBean); + } + return bean; + } + + private void scanPackages() throws IOException { + + ClassLoader cl = AnnotationDrivenEntitySchemaFactory.class.getClassLoader(); + + for (String packageToScan : _packagesToScan) { + + if (packageToScan != null) { + + String pkg = packageToScan.replace('.', '/'); + + for (Enumeration en = cl.getResources(pkg); en.hasMoreElements();) { + URL url = en.nextElement(); + + if (isJarURL(url)) { + + URL jarURL = extractJarFileURL(url); + File jarPath = getFile(jarURL); + JarFile jar = new JarFile(jarPath); + + for (Enumeration en2 = jar.entries(); en2.hasMoreElements();) { + JarEntry entry = en2.nextElement(); + + String name = entry.getName(); + if (name.startsWith(pkg) && name.endsWith(".class")) { + String cName = name.replace(".class", "").replace('/', '.'); + go(cName); + } + } + } else { + String path = URLDecoder.decode(url.getPath(), "UTF-8"); + + String root = new File(path.replace(pkg, "")).getAbsolutePath(); + if (!root.endsWith(File.separator)) + root += File.separator; + + scanFile(root, new File(path)); + } + } + } + } + } + + private void scanFile(String root, File f) { + if (f.isDirectory()) { + File[] files = f.listFiles(); + if (files != null) { + for (File fChild : files) + scanFile(root, fChild); + } + } else if (f.getName().endsWith(".class")) { + String cName = f.getAbsolutePath().replace(root, "").replace(".class", "").replace('/', '.'); + go(cName); + } + } + + /*************************************************************************** + * Classpath URL Wrangling Methods as pulled from + * + * org.springframework.util.ResourceUtils + * + * in the Spring Framework + * + * Copyright 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **************************************************************************/ + + /** + * Determine whether the given URL points to a resource in a jar file, that + * is, has protocol "jar", "zip", "wsjar" or "code-source". + *

+ * "zip" and "wsjar" are used by BEA WebLogic Server and IBM WebSphere, + * respectively, but can be treated like jar files. The same applies to + * "code-source" URLs on Oracle OC4J, provided that the path contains a jar + * separator. + * + * @param url the URL to check + * @return whether the URL has been identified as a JAR URL + */ + private static boolean isJarURL(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) + || URL_PROTOCOL_WSJAR.equals(protocol) || (URL_PROTOCOL_CODE_SOURCE.equals(protocol) && url.getPath().indexOf( + JAR_URL_SEPARATOR) != -1)); + } + + /** + * Extract the URL for the actual jar file from the given URL (which may point + * to a resource in a jar file or to a jar file itself). + * + * @param jarUrl the original URL + * @return the URL for the actual jar file + * @throws MalformedURLException if no valid jar file URL could be extracted + */ + private static URL extractJarFileURL(URL jarUrl) throws MalformedURLException { + String urlFile = jarUrl.getFile(); + int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR); + if (separatorIndex != -1) { + String jarFile = urlFile.substring(0, separatorIndex); + try { + return new URL(jarFile); + } catch (MalformedURLException ex) { + // Probably no protocol in original jar URL, like + // "jar:C:/mypath/myjar.jar". + // This usually indicates that the jar file resides in the file + // system. + if (!jarFile.startsWith("/")) { + jarFile = "/" + jarFile; + } + return new URL(FILE_URL_PREFIX + jarFile); + } + } else { + return jarUrl; + } + } + + private static File getFile(URL resourceUrl) throws FileNotFoundException { + if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) { + throw new FileNotFoundException("url cannot be resolved to absolute file path " + + "because it does not reside in the file system: " + resourceUrl); + } + try { + return new File(toURI(resourceUrl).getSchemeSpecificPart()); + } catch (URISyntaxException ex) { + // Fallback for URLs that are not valid URIs (should hardly ever + // happen). + return new File(resourceUrl.getFile()); + } + } + + /** + * Create a URI instance for the given URL, replacing spaces with "%20" quotes + * first. + *

+ * Furthermore, this method works on JDK 1.4 as well, in contrast to the + * URL.toURI() method. + * + * @param url the URL to convert into a URI instance + * @return the URI instance + * @throws URISyntaxException if the URL wasn't a valid URI + * @see java.net.URL#toURI() + */ + private static URI toURI(URL url) throws URISyntaxException { + return toURI(url.toString()); + } + + /** + * Create a URI instance for the given location String, replacing spaces with + * "%20" quotes first. + * + * @param location the location String to convert into a URI instance + * @return the URI instance + * @throws URISyntaxException if the location wasn't a valid URI + */ + private static URI toURI(String location) throws URISyntaxException { + return new URI(location.replaceAll(" ", "%20")); + } + +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BaseEntitySchema.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BaseEntitySchema.java new file mode 100644 index 000000000..c1df1893e --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BaseEntitySchema.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.ArrayList; +import java.util.List; + +public class BaseEntitySchema { + + private List _fields = new ArrayList(); + + private List _validators = new ArrayList(); + + private Class _entityClass; + + private List _fieldsInOrder = new ArrayList(); + + public BaseEntitySchema(Class entityClass) { + _entityClass = entityClass; + } + + public BaseEntitySchema(BaseEntitySchema schema) { + _fields.addAll(schema._fields); + _validators.addAll(schema._validators); + _entityClass = schema._entityClass; + _fieldsInOrder.addAll(schema._fieldsInOrder); + } + + public void addField(FieldMapping field) { + _fields.add(field); + } + + public void addValidator(EntityValidator entityValidator) { + _validators.add(entityValidator); + } + + public Class getEntityClass() { + return _entityClass; + } + + public List getFields() { + return _fields; + } + + public List getValidators() { + return _validators; + } + + public void setFieldsInOrder(List fieldsInOrder) { + _fieldsInOrder = fieldsInOrder; + } + + public List getFieldsInOrder() { + return _fieldsInOrder; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapper.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapper.java new file mode 100644 index 000000000..e1e720cff --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapper.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + + +public interface BeanWrapper { + + public Class getPropertyType(String propertyName); + + /** + * Get the current value of the specified property. + * + * @param propertyName the name of the property to get the value of (may be a + * nested path and/or an indexed/mapped property) + * @return the value of the property + * @throws InvalidPropertyException if there is no such property or if the + * property isn't readable + * @throws PropertyAccessException if the property was valid but the accessor + * method failed + */ + Object getPropertyValue(String propertyName); + + /** + * Set the specified value as current property value. + * + * @param propertyName the name of the property to set the value of (may be a + * nested path and/or an indexed/mapped property) + * @param value the new value + * @throws InvalidPropertyException if there is no such property or if the + * property isn't writable + * @throws PropertyAccessException if the property was valid but the accessor + * method failed or a type mismatch occured + */ + void setPropertyValue(String propertyName, Object value); + + public T getWrappedInstance(Class type); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapperFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapperFactory.java new file mode 100644 index 000000000..54fbaa61b --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/BeanWrapperFactory.java @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.beans.BeanInfo; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.onebusaway.csv_entities.exceptions.IntrospectionException; +import org.onebusaway.csv_entities.exceptions.MethodInvocationException; +import org.onebusaway.csv_entities.exceptions.NoSuchPropertyException; + +public class BeanWrapperFactory { + + private static Map, BeanClassWrapperImpl> _classWrappers = new HashMap, BeanClassWrapperImpl>(); + + public static BeanWrapper wrap(Object object) { + Class c = object.getClass(); + BeanClassWrapperImpl classWrapper = _classWrappers.get(c); + if (classWrapper == null) { + try { + BeanInfo beanInfo = java.beans.Introspector.getBeanInfo(c); + classWrapper = new BeanClassWrapperImpl(beanInfo); + _classWrappers.put(c, classWrapper); + } catch (Exception ex) { + throw new IntrospectionException(c); + } + } + + return new BeanWrapperImpl(classWrapper, object); + } + + private static class BeanClassWrapperImpl { + + private Map _readMethods = new HashMap(); + + private Map _writeMethods = new HashMap(); + + public BeanClassWrapperImpl(BeanInfo info) { + PropertyDescriptor[] properties = info.getPropertyDescriptors(); + for (PropertyDescriptor property : properties) { + String name = property.getName(); + _readMethods.put(name, property.getReadMethod()); + _writeMethods.put(name, property.getWriteMethod()); + } + } + + public Class getPropertyType(Object object, String propertyName) { + Method method = _readMethods.get(propertyName); + if (method == null) + throw new NoSuchPropertyException(object.getClass(), propertyName); + return method.getReturnType(); + } + + public Object getPropertyValue(Object object, String propertyName) { + Method method = _readMethods.get(propertyName); + if (method == null) + throw new NoSuchPropertyException(object.getClass(), propertyName); + try { + return method.invoke(object); + } catch (Exception ex) { + throw new MethodInvocationException(object.getClass(), method, ex); + } + } + + public void setPropertyValue(Object object, String propertyName, + Object value) { + Method method = _writeMethods.get(propertyName); + if (method == null) + throw new NoSuchPropertyException(object.getClass(), propertyName); + try { + method.invoke(object, value); + } catch (Exception ex) { + throw new MethodInvocationException(object.getClass(), method, ex); + } + } + } + + private static class BeanWrapperImpl implements BeanWrapper { + + private BeanClassWrapperImpl _classWrapper; + + private Object _wrappedInstance; + + public BeanWrapperImpl(BeanClassWrapperImpl classWrapper, + Object wrappedInstance) { + _classWrapper = classWrapper; + _wrappedInstance = wrappedInstance; + } + + @SuppressWarnings("unchecked") + public T getWrappedInstance(Class type) { + return (T) _wrappedInstance; + } + + public Class getPropertyType(String propertyName) { + return _classWrapper.getPropertyType(_wrappedInstance, propertyName); + } + + public Object getPropertyValue(String propertyName) { + return _classWrapper.getPropertyValue(_wrappedInstance, propertyName); + } + + public void setPropertyValue(String propertyName, Object value) { + _classWrapper.setPropertyValue(_wrappedInstance, propertyName, value); + } + + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DateFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DateFieldMappingFactory.java new file mode 100644 index 000000000..aae15fa8d --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DateFieldMappingFactory.java @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.CsvEntityException; +import org.onebusaway.csv_entities.exceptions.IntrospectionException; + +public class DateFieldMappingFactory implements FieldMappingFactory { + + @Override + public FieldMapping createFieldMapping(EntitySchemaFactory schemaFactory, + Class entityType, String csvFieldName, String objFieldName, + Class objFieldType, boolean required) { + + Field field = null; + try { + field = entityType.getDeclaredField(objFieldName); + } catch (Exception ex) { + throw new IntrospectionException(entityType,ex); + } + + DateFormatAnnotation formatAnnotation = field.getAnnotation(DateFormatAnnotation.class); + + if (formatAnnotation == null) { + throw new DateFieldMappingException(entityType, + "missing required @DateFormatAnnotation for field " + objFieldName + + " of type " + entityType); + } + + boolean isLongType = false; + + if (objFieldType == Long.class || objFieldType == Long.TYPE) + isLongType = true; + else if (objFieldType != Date.class) + throw new DateFieldMappingException(entityType, "expected that field " + + objFieldName + " of type " + entityType + + " is Date or long, but instead was " + objFieldType); + + DateFormat dateFormat = new SimpleDateFormat(formatAnnotation.value()); + return new FieldMappingImpl(entityType, csvFieldName, objFieldName, + required, dateFormat, isLongType); + } + + @Retention(value = RetentionPolicy.RUNTIME) + @Target(value = ElementType.FIELD) + public @interface DateFormatAnnotation { + public String value(); + } + + public static class DateFieldMappingException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public DateFieldMappingException(Class entityType, String message) { + super(entityType, message); + } + + public DateFieldMappingException(Class entityType, String message, + Throwable cause) { + super(entityType, message, cause); + } + } + + private static class FieldMappingImpl extends AbstractFieldMapping { + + private DateFormat _dateFormat; + private boolean _isLongType; + + public FieldMappingImpl(Class entityType, String csvFieldName, + String objFieldName, boolean required, DateFormat dateFormat, + boolean isLongType) { + super(entityType, csvFieldName, objFieldName, required); + _dateFormat = dateFormat; + _isLongType = isLongType; + } + + @Override + public void translateFromCSVToObject(CsvEntityContext context, + Map csvValues, BeanWrapper object) + throws CsvEntityException { + + if (isMissingAndOptional(csvValues)) + return; + + String dateAsString = (String) csvValues.get(_csvFieldName); + + try { + Date value = _dateFormat.parse(dateAsString); + + if (_isLongType) + object.setPropertyValue(_objFieldName, value.getTime()); + else + object.setPropertyValue(_objFieldName, value); + + } catch (ParseException e) { + throw new DateFieldMappingException(_entityType, + "error parsing data value " + dateAsString, e); + } + } + + @Override + public void translateFromObjectToCSV(CsvEntityContext context, + BeanWrapper object, Map csvValues) + throws CsvEntityException { + + if (isMissingAndOptional(object)) + return; + + Object obj = object.getPropertyValue(_objFieldName); + + Date date = null; + if (_isLongType) + date = new Date((Long) obj); + else + date = (Date) obj; + + String dateAsString = _dateFormat.format(date); + csvValues.put(_csvFieldName, dateAsString); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java new file mode 100644 index 000000000..2a6b0993c --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.CsvEntityException; +import org.onebusaway.csv_entities.exceptions.IntrospectionException; + +public class DecimalFieldMappingFactory implements FieldMappingFactory { + + private String _format; + + private Locale _locale = Locale.getDefault(); + + public DecimalFieldMappingFactory() { + + } + + public DecimalFieldMappingFactory(String format) { + _format = format; + } + + public DecimalFieldMappingFactory(String format, Locale locale) { + _format = format; + _locale = locale; + } + + @Override + public FieldMapping createFieldMapping(EntitySchemaFactory schemaFactory, + Class entityType, String csvFieldName, String objFieldName, + Class objFieldType, boolean required) { + + NumberFormat numberFormat = getFormat(entityType, objFieldName); + + return new FieldMappingImpl(entityType, csvFieldName, objFieldName, + objFieldType, required, numberFormat); + } + + private NumberFormat getFormat(Class entityType, String objFieldName) { + String format = determineFormat(entityType, objFieldName); + if (_locale == null) { + return new DecimalFormat(format); + } else { + return new DecimalFormat(format, new DecimalFormatSymbols(_locale)); + } + } + + @Retention(value = RetentionPolicy.RUNTIME) + @Target(value = ElementType.FIELD) + public @interface NumberFormatAnnotation { + public String value(); + } + + private String determineFormat(Class entityType, String objFieldName) { + if (_format != null) { + return _format; + } + + Field field = null; + try { + field = entityType.getDeclaredField(objFieldName); + } catch (Exception ex) { + throw new IntrospectionException(entityType, ex); + } + NumberFormatAnnotation formatAnnotation = field.getAnnotation(NumberFormatAnnotation.class); + + if (formatAnnotation == null) { + throw new DateFieldMappingException(entityType, + "missing required @DateFormatAnnotation for field " + objFieldName + + " of type " + entityType); + } + return formatAnnotation.value(); + } + + public static class DateFieldMappingException extends CsvEntityException { + + private static final long serialVersionUID = 1L; + + public DateFieldMappingException(Class entityType, String message) { + super(entityType, message); + } + + public DateFieldMappingException(Class entityType, String message, + Throwable cause) { + super(entityType, message, cause); + } + } + + private static class FieldMappingImpl extends DefaultFieldMapping { + + private NumberFormat _numberFormat; + + public FieldMappingImpl(Class entityType, String csvFieldName, + String objFieldName, Class objFieldType, boolean required, + NumberFormat numberFormat) { + super(entityType, csvFieldName, objFieldName, objFieldType, required); + _numberFormat = numberFormat; + } + + @Override + public void translateFromObjectToCSV(CsvEntityContext context, + BeanWrapper object, Map csvValues) + throws CsvEntityException { + + if (isMissingAndOptional(object)) + return; + + Number n = (Number) object.getPropertyValue(_objFieldName); + + String dateAsString = _numberFormat.format(n); + csvValues.put(_csvFieldName, dateAsString); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultConverter.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultConverter.java new file mode 100644 index 000000000..a7a30a15c --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultConverter.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import org.apache.commons.beanutils.Converter; + +public class DefaultConverter implements Converter { + + @Override + public Object convert(@SuppressWarnings("rawtypes") Class type, Object value) { + if (value == null) + return ""; + return value.toString(); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultEntitySchemaFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultEntitySchemaFactory.java new file mode 100644 index 000000000..2e1eea88b --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultEntitySchemaFactory.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class DefaultEntitySchemaFactory extends AbstractEntitySchemaFactoryImpl { + + private List _sources = new ArrayList(); + + public void addBean(CsvEntityMappingBean bean) { + _sources.add(new CsvEntityMappingBeanSource(bean)); + } + + public void addFactory(ListableCsvMappingFactory factory) { + _sources.add(new ListableCsvMappingFactorySource(factory)); + } + + /**** + * {@link AbstractEntitySchemaFactoryImpl} Interface + ****/ + + @Override + protected void processBeanDefinitions() { + for (BeanDefinitionSource source : _sources) + source.processBeanDefinitions(); + } + + private interface BeanDefinitionSource { + public void processBeanDefinitions(); + } + + private class CsvEntityMappingBeanSource implements BeanDefinitionSource { + + private CsvEntityMappingBean _bean; + + public CsvEntityMappingBeanSource(CsvEntityMappingBean bean) { + _bean = bean; + } + + public void processBeanDefinitions() { + registerBeanDefinition(_bean); + } + } + + private class ListableCsvMappingFactorySource implements BeanDefinitionSource { + + private ListableCsvMappingFactory _factory; + + public ListableCsvMappingFactorySource(ListableCsvMappingFactory factory) { + _factory = factory; + } + + public void processBeanDefinitions() { + Collection beans = _factory.getEntityMappings(); + for (CsvEntityMappingBean bean : beans) + registerBeanDefinition(bean); + } + } + +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultFieldMapping.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultFieldMapping.java new file mode 100644 index 000000000..4408a0fd2 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DefaultFieldMapping.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Map; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.Converter; +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.NoDefaultConverterException; + +public class DefaultFieldMapping extends AbstractFieldMapping { + + protected Class _objFieldType; + + private Converter _converter; + + public DefaultFieldMapping(Class entityType, String csvFieldName, + String objFieldName, Class objFieldType, boolean required) { + super(entityType, csvFieldName, objFieldName, required); + _objFieldType = objFieldType; + _converter = ConvertUtils.lookup(objFieldType); + if (_converter == null && objFieldType.equals(Object.class)) + _converter = new DefaultConverter(); + } + + public void translateFromCSVToObject(CsvEntityContext context, + Map csvValues, BeanWrapper object) { + + if (isMissingAndOptional(csvValues)) + return; + + Object csvValue = csvValues.get(_csvFieldName); + Object objValue = convertCsvValue(csvValue); + object.setPropertyValue(_objFieldName, objValue); + } + + public void translateFromObjectToCSV(CsvEntityContext context, + BeanWrapper object, Map csvValues) { + + if (isMissingAndOptional(object)) + return; + + Object objValue = object.getPropertyValue(_objFieldName); + csvValues.put(_csvFieldName, objValue); + } + + private Object convertCsvValue(Object csvValue) { + if (_converter != null) { + return _converter.convert(_objFieldType, csvValue); + } else if (csvValue != null + && _objFieldType.isAssignableFrom(csvValue.getClass())) { + return csvValue; + } else { + throw new NoDefaultConverterException(_entityType, _csvFieldName, + _objFieldName, _objFieldType); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchema.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchema.java new file mode 100644 index 000000000..960675049 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchema.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.ArrayList; +import java.util.List; + +public class EntitySchema extends BaseEntitySchema { + + private final String _filename; + + private final boolean _required; + + private List _extensions = new ArrayList(); + + public EntitySchema(Class entityClass, String filename, boolean required) { + super(entityClass); + _filename = filename; + _required = required; + } + + public EntitySchema(EntitySchema schema) { + super(schema); + _filename = schema._filename; + _required = schema._required; + _extensions = new ArrayList(schema._extensions); + } + + public String getFilename() { + return _filename; + } + + public boolean isRequired() { + return _required; + } + + public List getExtensions() { + return _extensions; + } + + public void addExtension(ExtensionEntitySchema extension) { + _extensions.add(extension); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactory.java new file mode 100644 index 000000000..cc4936b7e --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactory.java @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +public interface EntitySchemaFactory { + + public EntitySchema getSchema(Class entityClass); + +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactoryHelper.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactoryHelper.java new file mode 100644 index 000000000..0eae66cf0 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntitySchemaFactoryHelper.java @@ -0,0 +1,142 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.lang.reflect.Field; + +import org.onebusaway.csv_entities.exceptions.NoSuchPropertyException; +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; +import org.onebusaway.csv_entities.schema.beans.CsvFieldMappingBean; + +public class EntitySchemaFactoryHelper { + + private DefaultEntitySchemaFactory _factory; + + public EntitySchemaFactoryHelper(DefaultEntitySchemaFactory factory) { + _factory = factory; + } + + public CsvEntityMappingBean addEntity(Class entityClass) { + CsvEntityMappingBean bean = new CsvEntityMappingBean(entityClass); + _factory.addBean(bean); + return bean; + } + + public CsvEntityMappingBean addEntity(Class entityClass, String filename) { + CsvEntityMappingBean bean = addEntity(entityClass); + bean.setFilename(filename); + return bean; + } + + public CsvEntityMappingBean addEntity(Class entityClass, String filename, String prefix) { + CsvEntityMappingBean bean = addEntity(entityClass, filename); + bean.setPrefix(prefix); + return bean; + } + + public CsvFieldMappingBean addField(CsvEntityMappingBean entityBean, String fieldName) { + Class entityClass = entityBean.getType(); + try { + Field field = entityClass.getDeclaredField(fieldName); + CsvFieldMappingBean fieldBean = new CsvFieldMappingBean(field); + entityBean.addField(fieldBean); + return fieldBean; + } catch (Exception ex) { + throw new NoSuchPropertyException(entityClass, fieldName,ex); + } + } + + public CsvFieldMappingBean[] addFields(CsvEntityMappingBean entityBean, String... fieldNames){ + CsvFieldMappingBean[] fields = new CsvFieldMappingBean[fieldNames.length]; + for( int i=0; i + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; + +public interface EntityValidator { + + public int getOrder(); + + public void setOrder(int order); + + public void validateEntity(CsvEntityContext context, Map csvValues, BeanWrapper object); + + public void validateCSV(CsvEntityContext context, BeanWrapper object, Map csvValues); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntityValidatorFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntityValidatorFactory.java new file mode 100644 index 000000000..ec5fec0ef --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EntityValidatorFactory.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +public interface EntityValidatorFactory { + public EntityValidator createEntityValidator(EntitySchemaFactory schemaFactory); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EnumFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EnumFieldMappingFactory.java new file mode 100644 index 000000000..e7eccf547 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/EnumFieldMappingFactory.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.CsvException; + +public class EnumFieldMappingFactory implements FieldMappingFactory { + + public FieldMapping createFieldMapping(EntitySchemaFactory schemaFactory, + Class entityType, String csvFieldName, String objFieldName, + Class objFieldType, boolean required) { + + if (!objFieldType.isEnum()) { + throw new CsvException("expected enum type but found " + objFieldType); + } + + return new FieldMappingImpl(entityType, csvFieldName, objFieldName, + objFieldType, required); + } + + private class FieldMappingImpl extends DefaultFieldMapping { + + public FieldMappingImpl(Class entityType, String csvFieldName, + String objFieldName, Class objFieldType, boolean required) { + super(entityType, csvFieldName, objFieldName, objFieldType, required); + } + + @Override + public void translateFromCSVToObject(CsvEntityContext context, + Map csvValues, BeanWrapper object) { + @SuppressWarnings("rawtypes") + Class objFieldType = _objFieldType; + if (isMissingAndOptional(csvValues)) + return; + String value = csvValues.get(_csvFieldName).toString(); + @SuppressWarnings("unchecked") + Object v = Enum.valueOf(objFieldType, value); + object.setPropertyValue(_objFieldName, v); + } + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExcludeOptionalAndMissingEntitySchemaFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExcludeOptionalAndMissingEntitySchemaFactory.java new file mode 100644 index 000000000..3b8ee3ffa --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExcludeOptionalAndMissingEntitySchemaFactory.java @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.onebusaway.csv_entities.HasExtensions; +import org.onebusaway.csv_entities.exceptions.MissingRequiredEntityException; + +public class ExcludeOptionalAndMissingEntitySchemaFactory implements + EntitySchemaFactory { + + private final EntitySchemaFactory _source; + + private Map, EntitySchema> _schemas = new HashMap, EntitySchema>(); + + public ExcludeOptionalAndMissingEntitySchemaFactory(EntitySchemaFactory source) { + _source = source; + } + + public void scanEntities(Class entityClass, Iterable entities) { + EntitySchema schema = _source.getSchema(entityClass); + if (schema == null) { + return; + } + schema = new EntitySchema(schema); + List fields = schema.getFields(); + for (Iterator it = fields.iterator(); it.hasNext();) { + FieldMapping field = it.next(); + if (!field.isAlwaysIncludeInOutput() && allValuesAreMissingAndOptional(field, entities)) { + it.remove(); + } + } + for (ExtensionEntitySchema extensionSchema : schema.getExtensions()) { + fields = extensionSchema.getFields(); + for (Iterator it = fields.iterator(); it.hasNext();) { + FieldMapping field = it.next(); + if (!field.isAlwaysIncludeInOutput() && allExtensionValuesAreMissingAndOptional(field, extensionSchema.getEntityClass(), entities)) { + it.remove(); + } + } + } + _schemas.put(entityClass, schema); + } + + /**** + * {@link EntitySchemaFactory} + ****/ + + @Override + public EntitySchema getSchema(Class entityClass) { + EntitySchema schema = _schemas.get(entityClass); + if (schema != null) { + return schema; + } + return _source.getSchema(entityClass); + } + + /**** + * Private Methods + ****/ + + private boolean allValuesAreMissingAndOptional(FieldMapping field, + Iterable entities) { + for (Object entity : entities) { + if (fieldIsNotMissingOrOptional(field, entity)) return false; + } + return true; + } + + private boolean allExtensionValuesAreMissingAndOptional(FieldMapping field, + Class extensionType, + Iterable entities) { + for (Object entity : entities) { + if (entity instanceof HasExtensions) { + Object extension = ((HasExtensions) entity).getExtension(extensionType); + if (extension != null) { + if (fieldIsNotMissingOrOptional(field, extension)) return false; + } + } + } + return true; + } + + private boolean fieldIsNotMissingOrOptional(FieldMapping field, Object entity) { + BeanWrapper wrapped = BeanWrapperFactory.wrap(entity); + try { + if (!field.isMissingAndOptional(wrapped)) { + return true; + } + } catch (MissingRequiredEntityException ex) { + return true; + } + return false; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExtensionEntitySchema.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExtensionEntitySchema.java new file mode 100644 index 000000000..9e3671ff0 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ExtensionEntitySchema.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + + +public class ExtensionEntitySchema extends BaseEntitySchema { + + public ExtensionEntitySchema(Class entityClass) { + super(entityClass); + } + + public ExtensionEntitySchema(ExtensionEntitySchema schema) { + super(schema); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMapping.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMapping.java new file mode 100644 index 000000000..08d19532f --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMapping.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Collection; +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.CsvEntityException; + +public interface FieldMapping { + + public int getOrder(); + + public void getCSVFieldNames(Collection names); + + public void translateFromCSVToObject(CsvEntityContext context, + Map csvValues, BeanWrapper object) + throws CsvEntityException; + + public void translateFromObjectToCSV(CsvEntityContext context, + BeanWrapper object, Map csvValues) + throws CsvEntityException; + + public boolean isMissingAndOptional(Map csvValues); + + public boolean isMissingAndOptional(BeanWrapper object); + + public boolean isAlwaysIncludeInOutput(); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMappingFactory.java new file mode 100644 index 000000000..e59ae4220 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FieldMappingFactory.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + + +public interface FieldMappingFactory { + public FieldMapping createFieldMapping(EntitySchemaFactory schemaFactory, + Class entityType, String csvFieldName, String objFieldName, + Class objFieldType, boolean required); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMapping.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMapping.java new file mode 100644 index 000000000..33b261cab --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMapping.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + */ +package org.onebusaway.csv_entities.schema; + +import java.util.Collection; +import java.util.Map; + +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.exceptions.EntityInstantiationException; + +class FlattenFieldMapping extends AbstractFieldMapping { + + private Class _objFieldType; + + private EntitySchema _schema; + + public FlattenFieldMapping(Class entityType, String csvFieldName, + String objFieldName, Class objFieldType, boolean required, + EntitySchema schema) { + super(entityType, csvFieldName, objFieldName, required); + _objFieldType = objFieldType; + _schema = schema; + } + + public void getCSVFieldNames(Collection names) { + for (FieldMapping mapping : _schema.getFields()) + mapping.getCSVFieldNames(names); + } + + public void translateFromCSVToObject(CsvEntityContext context, + Map csvValues, BeanWrapper object) { + + Object id = getInstance(_objFieldType); + BeanWrapper wrapper = BeanWrapperFactory.wrap(id); + for (FieldMapping mapping : _schema.getFields()) + mapping.translateFromCSVToObject(context, csvValues, wrapper); + object.setPropertyValue(_objFieldName, id); + } + + public void translateFromObjectToCSV(CsvEntityContext context, + BeanWrapper object, Map csvValues) { + if( isMissingAndOptional(object)) + return; + Object id = object.getPropertyValue(_objFieldName); + BeanWrapper wrapper = BeanWrapperFactory.wrap(id); + for (FieldMapping mapping : _schema.getFields()) + mapping.translateFromObjectToCSV(context, wrapper, csvValues); + } + + private Object getInstance(Class type) { + try { + return type.newInstance(); + } catch (Exception ex) { + throw new EntityInstantiationException(type, ex); + } + } +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMappingFactory.java new file mode 100644 index 000000000..0e96d4dd2 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/FlattenFieldMappingFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +public class FlattenFieldMappingFactory implements FieldMappingFactory { + + public FieldMapping createFieldMapping(EntitySchemaFactory schemaFactory, Class entityType, String csvFieldName, + String objFieldName, Class objFieldType, boolean required) { + + EntitySchema schema = schemaFactory.getSchema(objFieldType); + return new FlattenFieldMapping(entityType, csvFieldName, objFieldName, objFieldType, required, schema); + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ListableCsvMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ListableCsvMappingFactory.java new file mode 100644 index 000000000..d45fd43fd --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/ListableCsvMappingFactory.java @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; + +import java.util.Collection; + +public interface ListableCsvMappingFactory { + public Collection getEntityMappings(); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/SingleFieldMapping.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/SingleFieldMapping.java new file mode 100644 index 000000000..225d59587 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/SingleFieldMapping.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +/** + * Defines a mapping between a single column of a CSV table and a single field + * of a Java bean. + * + * @author bdferris + * + */ +public interface SingleFieldMapping extends FieldMapping { + + /** + * + * @return the CSV name of the mapped field. + */ + public String getCsvFieldName(); + + /** + * + * @return the Java bean name of the mapped field. + */ + public String getObjFieldName(); +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvField.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvField.java new file mode 100644 index 000000000..aa0c85aca --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvField.java @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.onebusaway.csv_entities.schema.FieldMapping; +import org.onebusaway.csv_entities.schema.FieldMappingFactory; + +/** + * Annotates a field of a CSV entity Java class definition, defining how the + * Java field is serialized to and from a corresponding CSV field. + * + * For example, a Java field is considered "required" by default, meaning it + * must have a corresponding value in a CSV file. However, you can mark the + * field as "optional" with an annotation. + * + *
+ * {@literal @}CsvField(optional = true)
+ * private String someField;
+ * 
+ * + * See the various fields defined below for more details on how you can control + * the CSV <=> Java field mapping process. + * + * @author bdferris + * @see CsvFields + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = ElementType.FIELD) +public @interface CsvField { + /** + * If specified, the Java field value will be mapped from a CSV field with the + * specified name (as opposed to the default name automatically computed from + * the Java field name). + * + * @return + * @see CsvFieldNameConvention + * @see CsvFields#fieldNameConvention() + * @see CsvFields#prefix() + */ + String name() default ""; + + /** + * If true, the specified Java field will be ignored when mapping the object + * to and from CSV values. + * + * @return + */ + boolean ignore() default false; + + /** + * Specify a {@link FieldMappingFactory} class that will be used to construct + * a {@link FieldMapping} instance for mapping this Java field value to and + * from CSV values. The factory class must have a default constructor. + * + * @return + */ + Class mapping() default FieldMappingFactory.class; + + /** + * If false (the default case), a field is considered required and an error + * will be thrown if the field is missing in either the CSV or Java object + * when converting between the two. If true, then the field is considered + * optional and can be missing in the CSV or Java object. + * + * @return + */ + boolean optional() default false; + + /** + * Determines the order in which fields are processed, where fields with a + * smaller order value are processed first. When it is necessary to process + * one field before another (possibly due to inter-field dependencies), this + * override can be useful for setting the relative processing order of two + * fields. + * + * @return + */ + int order() default Integer.MAX_VALUE; + + /** + * Determines the default value for a field. Some CSV fields assume a default + * value when no value is specified in the CSV. By defining that default value + * for the field, we can automatically generate an empty value (or possibly + * exclude an entire column) when writing output CSV if all Java fields have + * the default value. + * + * Note: this does NOT set a default value in the Java object when the Csv + * field value is empty. + * + * @return + */ + String defaultValue() default ""; + + /** + * Determines if a field should always be included in CSV output, even if the + * Java field value is missing or matches the default empty value. + * + * @return + */ + boolean alwaysIncludeInOutput() default false; +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFieldNameConvention.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFieldNameConvention.java new file mode 100644 index 000000000..911270921 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFieldNameConvention.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema.annotations; + +/** + * Control how object field names are converted to CSV column header names. + * + * @author bdferris + * + */ +public enum CsvFieldNameConvention { + + /** + * The field name conversion is left unspecified. This is used as a default + * value for {@link CsvFields#fieldNameConvention()} and typically means the + * default behavior will be used: {@link #UNDERSCORE}. + */ + UNSPECIFIED, + + /** + * A field name like "thisIsTheName" is converted to "this_is_the_name". + */ + UNDERSCORE, + + /** + * A field name like "thisIsTheName" is left as "thisIsTheName". + */ + CAMEL_CASE, + + /** + * A field name like "thisIsTheName" is converted to "ThisIsTheName". + */ + CAPITALIZED_CAMEL_CASE +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFields.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFields.java new file mode 100644 index 000000000..67235866d --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/annotations/CsvFields.java @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a CSV entity Java class definition, control how entities of the + * specified type are serialized to and from a CSV file. + * + * @author bdferris + * @see CsvField + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = ElementType.TYPE) +public @interface CsvFields { + + /** + * Defines the CSV filename from which entities of this type should be read. + * + * @return + */ + String filename(); + + /** + * Defines the csv field name convention that determines how CSV field names + * are mapped to Java field names. + * + * @return + */ + CsvFieldNameConvention fieldNameConvention() default CsvFieldNameConvention.UNDERSCORE; + + /** + * Define a prefix string that will be prepended to ALL csv field names + * generated from Java field names for the class. For example, given a Java + * field name of "id" and a prefix of "stop_", the resulting CSV field name + * for the field would be "stop_id". Note that the prefix string is not used + * for any field defining a {@link CsvField#name()} annotation. + * + * @return a prefix string that will be prefix to ALL CSV field names + * generated from Java field names for the class. + */ + String prefix() default ""; + + /** + * If true, the corresponding CSV file for this Java entity type must exist in + * the input file set. If false, the CSV file is considered optional and may + * be unspecified. + * + * @return true if the CSV file / Java entity type is required. + */ + boolean required() default true; + + /** + * If a CSV file does not include a field name header as its first line, you + * may optionally define the header here. Specify a list of field names + * corresponding to each column of the CSV file. These CSV field names will be + * used when mapping the column values to Java fields. + * + * @return + */ + String[] fieldOrder() default {}; +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvEntityMappingBean.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvEntityMappingBean.java new file mode 100644 index 000000000..51e9d02dd --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvEntityMappingBean.java @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema.beans; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.onebusaway.csv_entities.schema.EntityValidator; +import org.onebusaway.csv_entities.schema.FieldMapping; +import org.onebusaway.csv_entities.schema.annotations.CsvFieldNameConvention; + +public class CsvEntityMappingBean { + + private final Class type; + + private boolean filenameSet = false; + + private String filename; + + private boolean prefixSet = false; + + private String prefix; + + private boolean requiredSet = false; + + private boolean required; + + private boolean autoGenerateSchemaSet = false; + + private boolean autoGenerateSchema; + + private CsvFieldNameConvention fieldNameConvention; + + private List _validators = new ArrayList(); + + private Map fields = new LinkedHashMap(); + + private List fieldsInOrder = new ArrayList(); + + private List additionalFieldMappings = new ArrayList(); + + public CsvEntityMappingBean(Class type) { + this.type = type; + } + + public Class getType() { + return type; + } + + public boolean isFilenameSet() { + return filenameSet; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filenameSet = true; + this.filename = filename; + } + + public boolean isPrefixSet() { + return prefixSet; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefixSet = true; + this.prefix = prefix; + } + + public boolean isRequiredSet() { + return requiredSet; + } + + public boolean isRequired() { + return required; + } + + public void setRequired(boolean required) { + this.requiredSet = true; + this.required = required; + } + + public boolean isAutoGenerateSchemaSet() { + return autoGenerateSchemaSet; + } + + public boolean isAutoGenerateSchema() { + return autoGenerateSchema; + } + + public void setAutoGenerateSchema(boolean autoGenerateSchema) { + this.autoGenerateSchemaSet = true; + this.autoGenerateSchema = autoGenerateSchema; + } + + public CsvFieldNameConvention getFieldNameConvention() { + return fieldNameConvention; + } + + public void setFieldNameConvention(CsvFieldNameConvention fieldNameConvention) { + this.fieldNameConvention = fieldNameConvention; + } + + public void addField(CsvFieldMappingBean field) { + this.fields.put(field.getField(), field); + } + + public Map getFields() { + return fields; + } + + public void addValidator(EntityValidator validator) { + _validators.add(validator); + } + + public List getValidators() { + return _validators; + } + + public void addAdditionalFieldMapping(FieldMapping fieldMapping) { + additionalFieldMappings.add(fieldMapping); + } + + public List getAdditionalFieldMappings() { + return additionalFieldMappings; + } + + public void addFieldInOrder(String fieldName) { + fieldsInOrder.add(fieldName); + } + + public List getFieldsInOrder() { + return fieldsInOrder; + } + + public void setFieldsInOrder(List fieldsInOrder) { + this.fieldsInOrder = fieldsInOrder; + } +} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvFieldMappingBean.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvFieldMappingBean.java new file mode 100644 index 000000000..aa2916185 --- /dev/null +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/beans/CsvFieldMappingBean.java @@ -0,0 +1,138 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema.beans; + +import java.lang.reflect.Field; + +import org.onebusaway.csv_entities.schema.FieldMappingFactory; + +public class CsvFieldMappingBean { + + private final Field field; + + private boolean nameSet = false; + private String name; + + private boolean ignoreSet = false; + private boolean ignore; + + private boolean optionalSet = false; + private boolean optional; + + private boolean alwaysIncludeInOutputSet = false; + private boolean alwaysIncludeInOutput = false; + + private boolean mappingSet = false; + private FieldMappingFactory mapping; + + private boolean orderSet = false; + private int order; + + private String defaultValue; + + public CsvFieldMappingBean(Field field) { + this.field = field; + } + + public Field getField() { + return field; + } + + public boolean isNameSet() { + return nameSet; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.nameSet = true; + this.name = name; + } + + public boolean isIgnoreSet() { + return ignoreSet; + } + + public boolean isIgnore() { + return ignore; + } + + public void setIgnore(boolean ignore) { + this.ignoreSet = true; + this.ignore = ignore; + } + + public boolean isOptionalSet() { + return optionalSet; + } + + public boolean isOptional() { + return optional; + } + + public void setOptional(boolean optional) { + this.optionalSet = true; + this.optional = optional; + } + + public boolean isAlwaysIncludeInOutputSet() { + return alwaysIncludeInOutputSet; + } + + public boolean isAlwaysIncludeInOutput() { + return alwaysIncludeInOutput; + } + + public void setAlwaysIncludeInOutput(boolean alwaysIncludeInOutput) { + this.alwaysIncludeInOutputSet = true; + this.alwaysIncludeInOutput = alwaysIncludeInOutput; + } + + public boolean isMappingSet() { + return mappingSet; + } + public FieldMappingFactory getMapping() { + return mapping; + } + + public void setMapping(FieldMappingFactory mapping) { + this.mappingSet = true; + this.mapping = mapping; + } + + public boolean isOrderSet() { + return orderSet; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.orderSet = true; + this.order = order; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } +} diff --git a/onebusaway-csv-entities/src/main/javadoc/overview.html b/onebusaway-csv-entities/src/main/javadoc/overview.html new file mode 100644 index 000000000..551633a22 --- /dev/null +++ b/onebusaway-csv-entities/src/main/javadoc/overview.html @@ -0,0 +1,115 @@ + + + +

The onebusaway-csv-entities project provides a library for + mapping between CSV data files and Java objects, both for reading and + writing.

+

Example

+ +

Consider the following CSV data file, "employees.csv": +

id,first_name,last_name,office,phone
+123,Alice,Brown,New York,555-1234
+456,Bob,Smith,San Francisco,
+ +

Here, our CSV data file defines a number of employee records, + with a header defining the fields of the file and each row + representing an employee. Let's say we wanted to map those employee + records to the following class:

+ +
class Employee {
+  private int id;
+  private String firstName;
+  private String lastName;
+  private String office;
+  private String phone;
+}
+ +

Schema Annotations

+ +

With a few simple annotations, we can define how to map the Java + class to and from CSV:

+ +
{@literal @}CsvFields(filename="employees.csv")
+class Employee {
+  private int id;
+  private String firstName;
+  private String lastName;
+  private String office;
+  {@literal @}CsvField(optional=true)
+  private String phone;
+}
+ +

+ The primary annotation is @CsvFields, + which indicates that the Java file can be mapped to and from entries + in a CSV file. We even define the name of the file that should be + written and read. +

+ +

+ By default, we map each field of the Java file to a field in the CSV + file. Each field is considered required by default, which means the + parser will throw an exception if a blank field value is found in the + input CSV (or in the Java object, when writing CSV). However, in our + example, not all phone number fields have been specified. So we + annotate the field with a @CsvField + annotation, marking the field as optional. @CsvField annotations can + be used to control how fields are serialized to and from CSV. +

+ +

Reading Objects

+ +

Now that we've annotated our Java class, let's see how to read + and build Java object instances from CSV data.

+ +
CsvEntityReader reader = new CsvEntityReader();
+reader.addEntityHandler(new EntityHandler() { ... });
+reader.readEntities(Employee.class, new FileReader("/path/to/employees.csv"));
+ +

+ First, we create a new CsvEntityReader + object, which will do the heavy-lifting of reading CSV data and + converting it into Java objects. Second, we register a new EntityHandler + instance. EntityHandler is just an interface with a single method, + "handleEntity", that gets called for each new Java entity created from + a CSV data row. Provide your own implementation to handle entities as + they are read and created. Finally, we direct the reader to read + entities of our annotated type from the specified file. +

+ +

Writing Objects

+ +

Writing entities is just as simple:

+ +
CsvEntityWriterFactory factory = new CsvEntityWriterFactory();
+CsvEntityWriter writer = factory.createWriter(Employee.class, new FileWriter("/path/to/employees.csv"));
+writer.handleEntity(new Employee());
+writer.close();
+ +

Next Steps

+ +

The OneBusAway CSV entities library can do a whole lot more than + reading and writing simple Java objects from CSV. Read the JavaDoc + documentation for more details.

+ + \ No newline at end of file diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/AnnotatedTestBean.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/AnnotatedTestBean.java new file mode 100644 index 000000000..fc3654853 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/AnnotatedTestBean.java @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; + +@CsvFields(filename = "test_beans") +public class AnnotatedTestBean { + + @CsvField(optional = false) + private String name; + + @CsvField(optional = true) + private String value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CSVLibraryTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CSVLibraryTest.java new file mode 100644 index 000000000..300ba84a9 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CSVLibraryTest.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2011 Brian Ferris + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class CSVLibraryTest { + + private CSVLibrary _csv; + + @Before + public void before() { + _csv = new CSVLibrary(); + } + + @Test + public void testParse() { + + List tokens = _csv.parse("a,b,c"); + assertEquals(3, tokens.size()); + assertEquals("a", tokens.get(0)); + assertEquals("b", tokens.get(1)); + assertEquals("c", tokens.get(2)); + + tokens = _csv.parse("a,\"b b\",\"c,c\""); + assertEquals(3, tokens.size()); + assertEquals("a", tokens.get(0)); + assertEquals("b b", tokens.get(1)); + assertEquals("c,c", tokens.get(2)); + + tokens = _csv.parse("b\"b"); + assertEquals(1, tokens.size()); + assertEquals("b\"b", tokens.get(0)); + } + + @Test + public void testParseWikipedia() { + + List tokens = _csv.parse("1997,Ford,E350"); + assertEquals(3, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals("Ford", tokens.get(1)); + assertEquals("E350", tokens.get(2)); + + tokens = _csv.parse("1997, Ford , E350"); + assertEquals(3, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals(" Ford ", tokens.get(1)); + assertEquals(" E350", tokens.get(2)); + + tokens = _csv.parse("1997,Ford,E350,\"Super, luxurious truck\""); + assertEquals(4, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals("Ford", tokens.get(1)); + assertEquals("E350", tokens.get(2)); + assertEquals("Super, luxurious truck", tokens.get(3)); + + tokens = _csv.parse("1997,Ford,E350,\"Super \"\"luxurious\"\" truck\""); + assertEquals(4, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals("Ford", tokens.get(1)); + assertEquals("E350", tokens.get(2)); + assertEquals("Super \"luxurious\" truck", tokens.get(3)); + + tokens = _csv.parse("1997,Ford,E350,\" Super luxurious truck \""); + assertEquals(4, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals("Ford", tokens.get(1)); + assertEquals("E350", tokens.get(2)); + assertEquals(" Super luxurious truck ", tokens.get(3)); + + tokens = _csv.parse("\"1997\",\"Ford\",\"E350\""); + assertEquals(3, tokens.size()); + assertEquals("1997", tokens.get(0)); + assertEquals("Ford", tokens.get(1)); + assertEquals("E350", tokens.get(2)); + } + + @Test + public void testParseWhitespace() { + List tokens = _csv.parse(" \"g\" "); + assertEquals(" \"g\" ", tokens.get(0)); + + tokens = _csv.parse(" \" h \" "); + assertEquals(" \" h \" ", tokens.get(0)); + + tokens = _csv.parse(" \" \"\" i \"\" \" "); + assertEquals(" \" \"\" i \"\" \" ", tokens.get(0)); + + tokens = _csv.parse(" \"a,b\",c"); + assertEquals(3, tokens.size()); + assertEquals(" \"a", tokens.get(0)); + assertEquals("b\"", tokens.get(1)); + assertEquals("c", tokens.get(2)); + } + + @Test + public void testTrimInitialWhitespace() { + + _csv.setTrimInitialWhitespace(true); + + List tokens = _csv.parse(" \"g\" "); + assertEquals("g ", tokens.get(0)); + + tokens = _csv.parse(" \" h \" "); + assertEquals(" h ", tokens.get(0)); + + tokens = _csv.parse(" \" \"\" i \"\" \" "); + assertEquals(" \" i \" ", tokens.get(0)); + + tokens = _csv.parse(" \"a,b\", c, \"d\""); + assertEquals(3, tokens.size()); + assertEquals("a,b", tokens.get(0)); + assertEquals("c", tokens.get(1)); + assertEquals("d", tokens.get(2)); + } +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CsvEntityReaderTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CsvEntityReaderTest.java new file mode 100644 index 000000000..f8e2a19c0 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/CsvEntityReaderTest.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.junit.Test; +import org.onebusaway.csv_entities.exceptions.CsvEntityIOException; +import org.onebusaway.csv_entities.schema.AnnotationDrivenEntitySchemaFactory; + +public class CsvEntityReaderTest { + + @Test + public void testBadLine() { + + CsvEntityReader reader = new CsvEntityReader(); + + AnnotationDrivenEntitySchemaFactory entitySchemaFactory = new AnnotationDrivenEntitySchemaFactory(); + entitySchemaFactory.addEntityClass(AnnotatedTestBean.class); + reader.setEntitySchemaFactory(entitySchemaFactory); + + String content = "name,value\na,b\n,d\n"; + StringReader source = new StringReader(content); + + try { + reader.readEntities(AnnotatedTestBean.class, source); + fail(); + } catch (CsvEntityIOException e) { + assertEquals(AnnotatedTestBean.class, e.getEntityType()); + assertEquals("test_beans", e.getPath()); + assertEquals(3, e.getLineNumber()); + } catch (IOException e) { + fail(); + } + } + + @Test + public void testInternString() throws CsvEntityIOException, IOException { + + ListEntityHandler handler = new ListEntityHandler(); + + CsvEntityReader reader = new CsvEntityReader(); + reader.setInternStrings(true); + reader.addEntityHandler(handler); + + String content = "name,value\na,b\nc,b\n"; + StringReader source = new StringReader(content); + reader.readEntities(AnnotatedTestBean.class, source); + + List values = handler.getValues(); + AnnotatedTestBean a = values.get(0); + AnnotatedTestBean b = values.get(1); + assertSame(a.getValue(), b.getValue()); + } + +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/ExtensionsTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/ExtensionsTest.java new file mode 100644 index 000000000..db74f97b0 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/ExtensionsTest.java @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import org.junit.Test; +import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.ExcludeOptionalAndMissingEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.util.Collections; +import java.util.List; +import java.util.zip.ZipFile; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ExtensionsTest { + + @Test + public void testReadExtensions() throws IOException { + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + factory.addExtension(BaseBean.class, ExtensionBean.class); + + ListEntityHandler handler = new ListEntityHandler(); + + CsvEntityReader reader = new CsvEntityReader(); + reader.setEntitySchemaFactory(factory); + reader.addEntityHandler(handler); + + String content = "name,value\nCats,untrustworthy\nDogs,awesome\n"; + reader.readEntities(BaseBean.class, new StringReader(content)); + + List beans = handler.getValues(); + assertEquals(2, beans.size()); + + { + BaseBean bean = beans.get(0); + assertEquals("Cats", bean.getName()); + ExtensionBean extension = bean.getExtension(ExtensionBean.class); + assertTrue(extension != null); + assertEquals("untrustworthy", extension.getValue()); + } + { + BaseBean bean = beans.get(1); + assertEquals("Dogs", bean.getName()); + ExtensionBean extension = bean.getExtension(ExtensionBean.class); + assertTrue(extension != null); + assertEquals("awesome", extension.getValue()); + } + } + + @Test + public void testWriteExtensions() throws IOException { + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + factory.addExtension(BaseBean.class, ExtensionBean.class); + + CsvEntityWriter writer = new CsvEntityWriter(); + writer.setEntitySchemaFactory(factory); + + File output = File.createTempFile("ExtensionsText", ".zip"); + output.delete(); + output.deleteOnExit(); + + writer.setOutputLocation(output); + + BaseBean bean = new BaseBean(); + bean.setName("Birds"); + ExtensionBean extension = new ExtensionBean(); + extension.setValue("flighty"); + bean.putExtension(ExtensionBean.class, extension); + + writer.handleEntity(bean); + writer.close(); + + ZipFile zip = new ZipFile(output); + String data = read(zip.getInputStream(zip.getEntry("animals.csv"))); + assertEquals("name,value\nBirds,flighty\n", data); + } + + @Test + public void testWriteEmptyExtensions() throws IOException { + BaseBean bean = new BaseBean(); + bean.setName("Birds"); + ExtensionBean extension = new ExtensionBean(); + extension.setValue("flighty"); + bean.putExtension(ExtensionBean.class, extension); + ExtensionBean2 extension2 = new ExtensionBean2(); + bean.putExtension(ExtensionBean2.class, extension2); + + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + factory.addExtension(BaseBean.class, ExtensionBean.class); + factory.addExtension(BaseBean.class, ExtensionBean2.class); + + ExcludeOptionalAndMissingEntitySchemaFactory excludeFactory = new ExcludeOptionalAndMissingEntitySchemaFactory(factory); + excludeFactory.scanEntities(BaseBean.class, Collections.singleton(bean)); + + CsvEntityWriter writer = new CsvEntityWriter(); + writer.setEntitySchemaFactory(excludeFactory); + + File output = File.createTempFile("ExtensionsText", ".zip"); + output.delete(); + output.deleteOnExit(); + + writer.setOutputLocation(output); + + writer.handleEntity(bean); + writer.close(); + + ZipFile zip = new ZipFile(output); + String data = read(zip.getInputStream(zip.getEntry("animals.csv"))); + assertEquals("name,value\nBirds,flighty\n", data); + } + + private static String read(InputStream in) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder b = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + b.append(line).append('\n'); + } + return b.toString(); + } + + @CsvFields(filename = "animals.csv") + public static class BaseBean extends HasExtensionsImpl { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class ExtensionBean { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static class ExtensionBean2 { + @CsvField(defaultValue = "0", optional = true) + private int emptyValue = 0; + + public int getEmptyValue() { + return emptyValue; + } + + public void setEmptyValue(int emptyValue) { + this.emptyValue = emptyValue; + } + } +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/HasExtensionsImpl.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/HasExtensionsImpl.java new file mode 100644 index 000000000..c229c7d58 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/HasExtensionsImpl.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2013 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import java.util.HashMap; +import java.util.Map; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; + +public class HasExtensionsImpl implements HasExtensions { + + @CsvField(ignore = true) + private Map, Object> extensions = new HashMap, Object>(); + + @Override + public void putExtension(Class type, Object extension) { + extensions.put(type, extension); + } + + @SuppressWarnings("unchecked") + @Override + public X getExtension(Class type) { + return (X) extensions.get(type); + } +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/IndividualCsvEntityWriterTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/IndividualCsvEntityWriterTest.java new file mode 100644 index 000000000..4045d238e --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/IndividualCsvEntityWriterTest.java @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +import static org.junit.Assert.assertEquals; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.Test; +import org.onebusaway.csv_entities.schema.DefaultEntitySchemaFactory; +import org.onebusaway.csv_entities.schema.EntitySchemaFactoryHelper; +import org.onebusaway.csv_entities.schema.beans.CsvEntityMappingBean; + +public class IndividualCsvEntityWriterTest { + + @Test + public void testOrder() { + + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + EntitySchemaFactoryHelper helper = new EntitySchemaFactoryHelper(factory); + + CsvEntityMappingBean mapping = helper.addEntity(TestBean.class); + helper.addField(mapping, "name"); + helper.addField(mapping, "value"); + + CsvEntityContextImpl context = new CsvEntityContextImpl(); + StringWriter output = new StringWriter(); + + IndividualCsvEntityWriter writer = new IndividualCsvEntityWriter(context, + factory.getSchema(TestBean.class), new PrintWriter(output)); + + TestBean bean = new TestBean(); + bean.setName("alice"); + bean.setValue("a"); + writer.handleEntity(bean); + + bean.setName("bob"); + bean.setValue("b"); + writer.handleEntity(bean); + + writer.close(); + + String content = output.getBuffer().toString(); + assertEquals("name,value\nalice,a\nbob,b\n", content); + } + + @Test + public void testOrderAlternate() { + + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + EntitySchemaFactoryHelper helper = new EntitySchemaFactoryHelper(factory); + + CsvEntityMappingBean mapping = helper.addEntity(TestBean.class); + helper.addField(mapping, "value"); + helper.addField(mapping, "name"); + + CsvEntityContextImpl context = new CsvEntityContextImpl(); + StringWriter output = new StringWriter(); + + IndividualCsvEntityWriter writer = new IndividualCsvEntityWriter(context, + factory.getSchema(TestBean.class), new PrintWriter(output)); + + TestBean bean = new TestBean(); + bean.setName("alice"); + bean.setValue("a"); + writer.handleEntity(bean); + + bean.setName("bob"); + bean.setValue("b"); + writer.handleEntity(bean); + + writer.close(); + + String content = output.getBuffer().toString(); + assertEquals("value,name\na,alice\nb,bob\n", content); + } + + @Test + public void testDefaultValues() { + DefaultEntitySchemaFactory factory = new DefaultEntitySchemaFactory(); + + CsvEntityContextImpl context = new CsvEntityContextImpl(); + StringWriter output = new StringWriter(); + + IndividualCsvEntityWriter writer = new IndividualCsvEntityWriter(context, + factory.getSchema(OptionalFieldTestBean.class), new PrintWriter(output)); + + OptionalFieldTestBean tb = new OptionalFieldTestBean(); + + tb.setIntValue(1234); + tb.setDoubleValue(2345.8); + + writer.handleEntity(tb); + + tb.clearIntValue(); + tb.clearDoubleValue(); + + writer.handleEntity(tb); + + writer.close(); + + String content = output.getBuffer().toString(); + assertEquals("int_value,double_value" + System.lineSeparator() + + "1234,2345.80" + System.lineSeparator() + "," + System.lineSeparator(), content); + } + +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/OptionalFieldTestBean.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/OptionalFieldTestBean.java new file mode 100644 index 000000000..164ae364c --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/OptionalFieldTestBean.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onebusaway.csv_entities; + +import org.onebusaway.csv_entities.schema.DecimalFieldMappingFactory; +import org.onebusaway.csv_entities.schema.DecimalFieldMappingFactory.NumberFormatAnnotation; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; + +@CsvFields(filename = "test_beans") +public class OptionalFieldTestBean { + + private static final int DEFAULT_VALUE = -999; + + @CsvField(optional = true) + private int intValue = DEFAULT_VALUE; + + @CsvField(optional = true, mapping = DecimalFieldMappingFactory.class) + @NumberFormatAnnotation("0.00") + private double doubleValue = DEFAULT_VALUE; + + public boolean isIntValueSet() { + return intValue != DEFAULT_VALUE; + } + + public int getIntValue() { + return intValue; + } + + public void setIntValue(final int intValue) { + this.intValue = intValue; + } + + public void clearIntValue() { + this.intValue = DEFAULT_VALUE; + } + + public boolean isDoubleValueSet() { + return doubleValue != DEFAULT_VALUE; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + public void clearDoubleValue() { + this.doubleValue = DEFAULT_VALUE; + } + +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/TestBean.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/TestBean.java new file mode 100644 index 000000000..97a330e6a --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/TestBean.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities; + +public class TestBean { + + private String name; + + private String value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} \ No newline at end of file diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImplTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImplTest.java new file mode 100644 index 000000000..27a6010aa --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/AbstractEntitySchemaFactoryImplTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2012 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.onebusaway.csv_entities.HasExtensionsImpl; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFieldNameConvention; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; + +public class AbstractEntitySchemaFactoryImplTest { + + @Test + public void test() { + SchemaFactory factory = new SchemaFactory(); + EntitySchema schema = factory.getSchema(CapitalizedCamelCaseBean.class); + List fields = schema.getFields(); + assertEquals(1, fields.size()); + SingleFieldMapping fieldMapping = (SingleFieldMapping) fields.get(0); + assertEquals("FirstName", fieldMapping.getCsvFieldName()); + assertEquals("firstName", fieldMapping.getObjFieldName()); + } + + @Test + public void testExtensions() { + SchemaFactory factory = new SchemaFactory(); + factory.addExtension(BaseBean.class, ExtensionBean.class); + + EntitySchema schema = factory.getSchema(BaseBean.class); + List extensions = schema.getExtensions(); + assertEquals(1, extensions.size()); + ExtensionEntitySchema extensionSchema = extensions.get(0); + List fields = extensionSchema.getFields(); + assertEquals(1, fields.size()); + SingleFieldMapping field = (SingleFieldMapping) fields.get(0); + assertEquals("value", field.getCsvFieldName()); + } + + @Test + public void testExtensionsAfterSchemaIsCached() { + SchemaFactory factory = new SchemaFactory(); + EntitySchema entitySchema = factory.getSchema(BaseBean.class); + assertTrue(entitySchema.getExtensions().isEmpty()); + + factory.addExtension(BaseBean.class, ExtensionBean.class); + + entitySchema = factory.getSchema(BaseBean.class); + assertEquals(1, entitySchema.getExtensions().size()); + } + + private class SchemaFactory extends AbstractEntitySchemaFactoryImpl { + @Override + protected void processBeanDefinitions() { + + } + } + + @CsvFields(filename = "file.csv", fieldNameConvention = CsvFieldNameConvention.CAPITALIZED_CAMEL_CASE) + public static class CapitalizedCamelCaseBean { + + private String firstName; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + } + + public static class BaseBean extends HasExtensionsImpl { + + } + + public static class ExtensionBean { + @CsvField(optional = true) + private String value; + } +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/BeanWrapperFactoryTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/BeanWrapperFactoryTest.java new file mode 100644 index 000000000..b75110550 --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/BeanWrapperFactoryTest.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2011 Brian Ferris + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onebusaway.csv_entities.schema.BeanWrapper; +import org.onebusaway.csv_entities.schema.BeanWrapperFactory; + +public class BeanWrapperFactoryTest { + + @Test + public void test() { + AB ab = new AB(); + ab.setA("a"); + ab.setB("b"); + + BeanWrapper wrapper = BeanWrapperFactory.wrap(ab); + assertEquals(ab, wrapper.getWrappedInstance(AB.class)); + + assertEquals("a", wrapper.getPropertyValue("a")); + assertEquals("b", wrapper.getPropertyValue("b")); + + ab.setA("c"); + ab.setB("d"); + + assertEquals("c", wrapper.getPropertyValue("a")); + assertEquals("d", wrapper.getPropertyValue("b")); + + wrapper.setPropertyValue("a", "e"); + wrapper.setPropertyValue("b", "f"); + + assertEquals("e",ab.getA()); + assertEquals("f",ab.getB()); + + assertEquals("e", wrapper.getPropertyValue("a")); + assertEquals("f", wrapper.getPropertyValue("b")); + } + + private static class AB { + + private String a; + + private String b; + + public String getA() { + return a; + } + + public void setA(String a) { + this.a = a; + } + + public String getB() { + return b; + } + + public void setB(String b) { + this.b = b; + } + + } +} diff --git a/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactoryTest.java b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactoryTest.java new file mode 100644 index 000000000..021b11e3c --- /dev/null +++ b/onebusaway-csv-entities/src/test/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactoryTest.java @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2011 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.csv_entities.schema; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.junit.Test; +import org.onebusaway.csv_entities.CsvEntityContext; +import org.onebusaway.csv_entities.CsvEntityContextImpl; + +public class DecimalFieldMappingFactoryTest { + + @Test + public void testWithEnLocale() { + DecimalFieldMappingFactory factory = new DecimalFieldMappingFactory("0.00", Locale.US); + FieldMapping mapping = createFieldMapping(factory); + + Dummy dummy = new Dummy(); + dummy.setValue(3.14159); + + CsvEntityContext context = new CsvEntityContextImpl(); + BeanWrapper wrapped = BeanWrapperFactory.wrap(dummy); + Map values = new HashMap(); + mapping.translateFromObjectToCSV(context, wrapped, values); + assertEquals("3.14", values.get("value")); + } + + @Test + public void testWithFrLocale() { + DecimalFieldMappingFactory factory = new DecimalFieldMappingFactory("0.00", + Locale.FRANCE); + FieldMapping mapping = createFieldMapping(factory); + + Dummy dummy = new Dummy(); + dummy.setValue(3.14159); + + CsvEntityContext context = new CsvEntityContextImpl(); + BeanWrapper wrapped = BeanWrapperFactory.wrap(dummy); + Map values = new HashMap(); + mapping.translateFromObjectToCSV(context, wrapped, values); + assertEquals("3,14", values.get("value")); + } + + private FieldMapping createFieldMapping(DecimalFieldMappingFactory factory) { + EntitySchemaFactory schemaFactory = new DefaultEntitySchemaFactory(); + FieldMapping mapping = factory.createFieldMapping(schemaFactory, + Dummy.class, "value", "value", Double.TYPE, true); + return mapping; + } + + public static class Dummy { + private double value; + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } + + } +} From 01196da935273489ad862541a37ea95ca8ccf063 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:46:30 +0200 Subject: [PATCH 107/234] Add module to list --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index fd1f53dbd..9a4f7d9e6 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,7 @@ + onebusaway-csv-entities onebusaway-collections onebusaway-gtfs onebusaway-gtfs-hibernate From b557a0454787c21eb9768eb10c25801dedd741a7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:47:28 +0200 Subject: [PATCH 108/234] Upgrade commons-beanutils --- onebusaway-csv-entities/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index fff566b9a..fcafd4bfc 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -20,7 +20,7 @@ commons-beanutils commons-beanutils - 1.7.0 + 1.9.4 org.slf4j From 6df0a07fb3dc0afffca5c19a1b5db915ac8c71a1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:54:27 +0200 Subject: [PATCH 109/234] Finalise POM setup --- onebusaway-gtfs-transformer-cli/pom.xml | 6 ------ onebusaway-gtfs-transformer/pom.xml | 4 ++-- onebusaway-gtfs/pom.xml | 7 +------ pom.xml | 11 ++--------- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index ccaacca65..f966b2182 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -12,12 +12,6 @@ 3.1.1-SNAPSHOT - - - false - - org.onebusaway diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a60d23270..1100884c9 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -51,12 +51,12 @@ org.onebusaway onebusaway-cloud-api - ${onebusaway_cloud_version} + ${onebusaway.cloud.version} org.onebusaway onebusaway-cloud-noop - ${onebusaway_cloud_version} + ${onebusaway.cloud.version} javax.xml.bind diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 376cf1fb3..9aa4c00f8 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -17,12 +17,7 @@ org.onebusaway onebusaway-csv-entities - - - slf4j-api - org.slf4j - - + ${project.version} org.slf4j diff --git a/pom.xml b/pom.xml index 9a4f7d9e6..3a844b4fc 100644 --- a/pom.xml +++ b/pom.xml @@ -29,9 +29,8 @@ UTF-8 UTF-8 - 1.1.7 2.0.16 - 0.0.13 + 0.0.13 5.11.0 @@ -84,13 +83,7 @@ org.onebusaway onebusaway-csv-entities - ${onebusaway_csv_entities_version} - - - slf4j-api - org.slf4j - - + ${project.version} org.slf4j From 727d189ce86a746e1203504eb89b2a3356075642 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:55:36 +0200 Subject: [PATCH 110/234] Remove META-INF --- onebusaway-gtfs/src/main/java/META-INF/MANIFEST.MF | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 onebusaway-gtfs/src/main/java/META-INF/MANIFEST.MF diff --git a/onebusaway-gtfs/src/main/java/META-INF/MANIFEST.MF b/onebusaway-gtfs/src/main/java/META-INF/MANIFEST.MF deleted file mode 100644 index 5e9495128..000000000 --- a/onebusaway-gtfs/src/main/java/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - From 9655fda246a376c983332614c06e69a819096a4b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:59:12 +0000 Subject: [PATCH 111/234] Update dependency io.github.classgraph:classgraph to v4.8.112 [SECURITY] --- onebusaway-collections/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 909fe7e00..1cd641daa 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -25,7 +25,7 @@ io.github.classgraph classgraph - 4.8.105 + 4.8.112 From b5111252e969e5658aab623e70a0c2a6cef48f78 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:07:12 +0000 Subject: [PATCH 112/234] Update dependency io.github.classgraph:classgraph to v4.8.175 --- onebusaway-collections/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 1cd641daa..c3a1ed43b 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -25,7 +25,7 @@ io.github.classgraph classgraph - 4.8.112 + 4.8.175 From d4e882034d08cef0ce4873ee44c9e89ab3444316 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:19:40 +0200 Subject: [PATCH 113/234] [maven-release-plugin] prepare release v3.2.0 --- onebusaway-collections/pom.xml | 5 ++--- onebusaway-csv-entities/pom.xml | 5 ++--- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 13 insertions(+), 15 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index c3a1ed43b..a29758ae6 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 onebusaway-collections @@ -11,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index fcafd4bfc..966b0ce9a 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 257817ed8..ef88aae5d 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 64d019783..b3d97baf0 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 003300ff2..b4a711000 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.1.1-SNAPSHOT + 3.2.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 54f1f0606..5f75ff978 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.1.1-SNAPSHOT + 3.2.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index f966b2182..3ce898ee3 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 1100884c9..4ac1405d2 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 9aa4c00f8..074b33083 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 7aa0dfd37..4a14edaa7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.1.1-SNAPSHOT + 3.2.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v3.2.0 From 1c58f2a15f63f368e8e351a9c9334473ad0e1963 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:19:42 +0200 Subject: [PATCH 114/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index a29758ae6..1fa9211b5 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 966b0ce9a..b8d55461f 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index ef88aae5d..9a18511a0 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index b3d97baf0..c5a38ef88 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index b4a711000..176c25bb7 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.0 + 3.2.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 5f75ff978..c979898e9 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.0 + 3.2.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 3ce898ee3..85bee9b82 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 4ac1405d2..abc7e9dbc 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 074b33083..25fa258bb 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 4a14edaa7..0e2bb1e0c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.0 + 3.2.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v3.2.0 + HEAD From ceb125559b35e4abca31cee2e42364e8e53f315f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:48:47 +0200 Subject: [PATCH 115/234] Move Camsys Repo to where it is really needed --- onebusaway-gtfs-transformer/pom.xml | 12 ++++++++++++ pom.xml | 19 ------------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index abc7e9dbc..e2f0af485 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -12,6 +12,18 @@ 3.2.1-SNAPSHOT + + + central2 + Check central first to avoid a lot of not found warnings + https://repo.maven.apache.org/maven2 + + + releases-camsys-public-repo + https://repo.camsys-apps.com/releases/ + + + org.onebusaway diff --git a/pom.xml b/pom.xml index 0e2bb1e0c..f1c0e7ab1 100644 --- a/pom.xml +++ b/pom.xml @@ -46,25 +46,6 @@ https://github.com/OneBusAway/onebusaway-gtfs-modules/issues - - - central2 - Check central first to avoid a lot of not found warnings - https://repo.maven.apache.org/maven2 - - - repo.camsys-apps.com - https://repo.camsys-apps.com/third-party/ - - - releases-camsys-public-repo - https://repo.camsys-apps.com/releases/ - - - snapshots-camsys-public-repo - https://repo.camsys-apps.com/snapshots/ - - onebusaway-csv-entities From 935dd193ef370a616cee0137b6a45b82caced0f5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:57:22 +0200 Subject: [PATCH 116/234] [maven-release-plugin] prepare release v3.2.2 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 1fa9211b5..ac77edeaf 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index b8d55461f..7fdcae32b 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 9a18511a0..9411a92cb 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index c5a38ef88..4028ea277 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 176c25bb7..4e6a7d12c 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.1-SNAPSHOT + 3.2.2 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index c979898e9..84c27b4b3 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.1-SNAPSHOT + 3.2.2 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 85bee9b82..8f5be983a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index e2f0af485..127d3f466 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 25fa258bb..9c4b889b8 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 ../pom.xml diff --git a/pom.xml b/pom.xml index f1c0e7ab1..4d4da1881 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.1-SNAPSHOT + 3.2.2 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v3.2.2 From d3d7f5e568ef387bd1c008605ea73f30a3af6329 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:57:23 +0200 Subject: [PATCH 117/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index ac77edeaf..26673f1e2 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 7fdcae32b..fbf0471ff 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 9411a92cb..0e1ba7ca1 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 4028ea277..fec0c76dd 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 4e6a7d12c..12c0eaa58 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.2 + 3.2.3-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 84c27b4b3..edd7ddc30 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.2 + 3.2.3-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 8f5be983a..d4cc2c087 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 127d3f466..453c507ab 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 9c4b889b8..a4a793901 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 4d4da1881..e2f6611b6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.2 + 3.2.3-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v3.2.2 + HEAD From 348594ee857428cdf9ca549d2424822c53e9f2df Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 00:58:52 +0200 Subject: [PATCH 118/234] Add instructions to Makefile --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 69e45b7ad..7cbf3f563 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,4 @@ release: + git checkout master + git pull mvn release:clean release:prepare release:perform -Dgoals=deploy release:clean From 21a4b1325522f1af06f74ddc8160530b46cd72bb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Sep 2024 14:47:40 +0200 Subject: [PATCH 119/234] Update merge-cli docs --- docs/onebusaway-gtfs-merge-cli.md | 93 ++++++++++++------------------- 1 file changed, 37 insertions(+), 56 deletions(-) diff --git a/docs/onebusaway-gtfs-merge-cli.md b/docs/onebusaway-gtfs-merge-cli.md index 66289a908..9d5038b97 100644 --- a/docs/onebusaway-gtfs-merge-cli.md +++ b/docs/onebusaway-gtfs-merge-cli.md @@ -1,29 +1,21 @@ # GTFS Merge Command-Line Application -Introduction +## Introduction - <>: This tool is a work in progress! This documentation may not be up-to-date. - - The `onebusaway-gtfs-merge-cli` command-line application is a simple command-line tool for merging +The `onebusaway-gtfs-merge-cli` command-line application is a simple command-line tool for merging [GTFS](https://developers.google.com/transit/gtfs) feeds. -Getting the Application +## Getting the Application - You can download the application here: +You can download the application from Maven Central. -#if( $currentVersion.endsWith("-SNAPSHOT")) - #set( $repository = 'snapshots' ) -#else - #set( $repository = 'releases' ) -#end +Go to https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-merge-cli/, select the version +you want and get the URL for the largest jar file. An example would be +https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-merge-cli/3.2.2/onebusaway-gtfs-merge-cli-3.2.2.jar -#set( $url = 'https://repo.camsys-apps.com/' + $repository + '/org/onebusaway/onebusaway-gtfs-merge-cli/' + ${currentVersion} + '/onebusaway-gtfs-merge-cli-' + ${currentVersion} + '.jar' ) - - [.jar](${url}}onebusaway-gtfs-merge-cli-${currentVersion) - -Using the Application +## Using the Application - You'll need a Java 11 runtime installed to run the client. To run the application: +You'll need a Java 17 runtime installed to run the client. To run the application: ``` java -jar onebusaway-gtfs-merge-cli.jar [--args] input_gtfs_path_a input_gtfs_path_b ... output_gtfs_path @@ -45,84 +37,73 @@ specific options for each file type after the `--file` option. Here's a quick e The merge application supports merging the following files: - * `agency.txt` - - * `stops.txt` - - * `routes.txt` - - * `trips.txt` and `stop_times.txt` - - * `calendar.txt` and `calendar_dates.txt` - - * `shapes.txt` - - * `fare_attributes.txt` - - * `fare_rules.txt` - - * `frequencies.txt` - - * `transfers.txt` - - [] + - `agency.txt` + - `stops.txt` + - `routes.txt` + - `trips.txt` and `stop_times.txt` + - `calendar.txt` and `calendar_dates.txt` + - `shapes.txt` + - `fare_attributes.txt` + - `fare_rules.txt` + - `frequencies.txt` + - `transfers.txt` - You can specify merge options for each of these files using the `--file=gtfs_file.txt` option. File types listed +You can specify merge options for each of these files using the `--file=gtfs_file.txt` option. File types listed together (eg. `trips.txt>> and `stop_times.txt`) are handled by the same merge strategy, so specifying options for either will have the same effect. For details on options you might specify, read on. -Handling Duplicates +## Handling Duplicates - The main issue to considering when merging GTFS feeds is the handling of duplicate entries between the two feeds, +The main issue to considering when merging GTFS feeds is the handling of duplicate entries between the two feeds, including how to identify duplicates and what to do with duplicates when they are found. -* Identifying Duplicates +### Identifying Duplicates - We support a couple of methods for determining when entries from two different feeds are actually duplicates. By default, +We support a couple of methods for determining when entries from two different feeds are actually duplicates. By default, the merge tool will attempt to automatically determine the best merge strategy to use. You can also control the specific strategy used on a per-file basis using the `--duplicateDetection` argument. You can specify any of the following strategies for duplicate detection. - * `--duplicateDetection=identity` - If two entries have the same id (eg. stop id, route id, trip id), then they are + - `--duplicateDetection=identity` - If two entries have the same id (eg. stop id, route id, trip id), then they are considered the same. This is the more strict matching policy. - * `--duplicateDetection=fuzzy` - If two entries have common elements (eg. stop name or location, route short name, + - `--duplicateDetection=fuzzy` - If two entries have common elements (eg. stop name or location, route short name, trip stop sequence), then they are considered the same. This is the more lenient matching policy, and is highly dependent on the type of GTFS entry being matched. - * `--duplicateDetection=none` - Entries between two feeds are never considered to be duplicates, even if they have + - `--duplicateDetection=none` - Entries between two feeds are never considered to be duplicates, even if they have the same id or similar properties. -* Logging Duplicates +### Logging Duplicates - Sometimes your feed might have unexpected duplicates. You can tell the merge tool to log duplicates it finds or even +Sometimes your feed might have unexpected duplicates. You can tell the merge tool to log duplicates it finds or even immediately exit with the following arguments: - * `--logDroppedDuplicates` - log a message when a duplicate is found + - `--logDroppedDuplicates` - log a message when a duplicate is found - * `--errorOnDroppedDuplicates` - throw an exception when a duplicate is found, stopping the program + - `--errorOnDroppedDuplicates` - throw an exception when a duplicate is found, stopping the program -Examples +## Examples -* Handling a Service Change +### Handling a Service Change - Agencies often schedule major changes to their system around a particular date, with one GTFS feed for before the +Agencies often schedule major changes to their system around a particular date, with one GTFS feed for before the service change and a different GTFS feed for after. We'd like to be able to merge these disjoint feeds into one feed with continuous coverage. - In our example, an agency produces two feeds where the entries in `agency.txt` and `stops.txt` are exactly +In our example, an agency produces two feeds where the entries in `agency.txt` and `stops.txt` are exactly the same, so the default policy of identifying and dropping duplicates will work fine there. The `routes.txt` file is a bit trickier, since the route ids are different between the two feeds but the entries are largely the same. We will use fuzzy duplicate detection to match the routes between the two feeds. - The next issue is the `calendar.txt` file. The agency uses the same `service_id` values in both feeds +The next issue is the `calendar.txt` file. The agency uses the same `service_id` values in both feeds (eg. `WEEK`, `SAT`, `SUN`) with different start and end dates in the two feeds. If the default policy of dropping duplicate entries was used, we'd lose the dates in one of the service periods. Instead, we rename duplicates such that the service ids from the second feed will be renamed to `b-WEEK`, `b-SAT`, etc. and all `trips.txt` entries in the second feed will be updated appropriately. The result is that trips from the first and second feed will both have the proper calendar entries in the merged feed. - Putting it all together, here is what the command-line options for the application would look like: +Putting it all together, here is what the command-line options for the application would look like: ``` --file=routes.txt --fuzzyDuplicates --file=calendar.txt --renameDuplicates From c862e20f74657fca764bb58a5be90a88dc70d175 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 09:43:31 +0200 Subject: [PATCH 120/234] Add locale in github action --- .github/workflows/ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1467afae9..448a55ad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,30 @@ jobs: runs-on: ubuntu-latest + strategy: + matrix: + locale: [ "en_US.utf8", "fr_FR.utf8" ] + + env: + LANG: ${{ matrix.locale }} + steps: - uses: actions/checkout@v4 + - name: Set locale + run: | + + lang=`echo "${{ matrix.locale }}" | head -c 2` + echo $lang + + sudo apt-get install -y language-pack-${lang} + # list installed locales + locale -a + sudo locale-gen ${{ matrix.locale }} + sudo update-locale LANG=${{ matrix.locale }} + + - run: date + - uses: actions/setup-java@v4 with: distribution: 'temurin' From cec02e75db9238768d348db6aa949272fcfe5647 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:29:02 +0200 Subject: [PATCH 121/234] Small code cleanup --- .github/workflows/ci.yml | 8 +++----- .../csv_entities/IndividualCsvEntityWriter.java | 10 +++++----- .../schema/DecimalFieldMappingFactory.java | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 448a55ad0..b5f472b40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,17 +16,15 @@ jobs: env: LANG: ${{ matrix.locale }} - + steps: - uses: actions/checkout@v4 - - name: Set locale + - name: Set locale to ${{ matrix.locale }} run: | - lang=`echo "${{ matrix.locale }}" | head -c 2` - echo $lang - sudo apt-get install -y language-pack-${lang} + # list installed locales locale -a sudo locale-gen ${{ matrix.locale }} diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java index e6140156b..400af867a 100644 --- a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/IndividualCsvEntityWriter.java @@ -32,13 +32,13 @@ class IndividualCsvEntityWriter implements EntityHandler { - private PrintWriter _writer; + private final PrintWriter _writer; - private List _fieldNames = new ArrayList(); + private final List _fieldNames = new ArrayList(); - private EntitySchema _schema; + private final EntitySchema _schema; - private CsvEntityContext _context; + private final CsvEntityContext _context; private TokenizerStrategy _tokenizerStrategy = new CsvTokenizerStrategy(); @@ -78,7 +78,7 @@ public void handleEntity(Object object) { } BeanWrapper wrapper = BeanWrapperFactory.wrap(object); - Map csvValues = new HashMap(); + Map csvValues = new HashMap<>(); for (FieldMapping field : _schema.getFields()) { field.translateFromObjectToCSV(_context, wrapper, csvValues); } diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java index 2a6b0993c..7541972cb 100644 --- a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java @@ -73,7 +73,7 @@ private NumberFormat getFormat(Class entityType, String objFieldName) { @Retention(value = RetentionPolicy.RUNTIME) @Target(value = ElementType.FIELD) public @interface NumberFormatAnnotation { - public String value(); + String value(); } private String determineFormat(Class entityType, String objFieldName) { From a2370173e51db6e65c35028a84d9a8ad29414b11 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:31:41 +0200 Subject: [PATCH 122/234] Set default locale to US --- .../csv_entities/schema/DecimalFieldMappingFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java index 7541972cb..8f73f6089 100644 --- a/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java +++ b/onebusaway-csv-entities/src/main/java/org/onebusaway/csv_entities/schema/DecimalFieldMappingFactory.java @@ -35,7 +35,7 @@ public class DecimalFieldMappingFactory implements FieldMappingFactory { private String _format; - private Locale _locale = Locale.getDefault(); + private Locale _locale = Locale.US; public DecimalFieldMappingFactory() { From b5f68ffa251f76b719c9af6cfd41f7f35471217a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:35:48 +0200 Subject: [PATCH 123/234] Finetune action script --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5f472b40..b30e6de87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,9 +23,12 @@ jobs: - name: Set locale to ${{ matrix.locale }} run: | lang=`echo "${{ matrix.locale }}" | head -c 2` - sudo apt-get install -y language-pack-${lang} + sudo apt-get -qq install -y language-pack-${lang} + + echo "" # list installed locales + echo "Available locales" locale -a sudo locale-gen ${{ matrix.locale }} sudo update-locale LANG=${{ matrix.locale }} From 6d06ed019c3e23b85bda92a641f9179b54fe6995 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:45:52 +0200 Subject: [PATCH 124/234] Set plugin versions in pluginManagement --- pom.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2f6611b6..e86b2fa37 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.onebusaway @@ -198,6 +199,21 @@ + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.3 + + + From 397e1875a86e3b7fc6fda93e808c92ee53b33643 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:54:33 +0200 Subject: [PATCH 125/234] [maven-release-plugin] prepare release v3.2.3 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 7 +++---- 10 files changed, 12 insertions(+), 13 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 26673f1e2..008583644 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index fbf0471ff..81fb228ed 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 0e1ba7ca1..d2f7a8d9d 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index fec0c76dd..33830de0c 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 12c0eaa58..c81c4bb5f 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.3-SNAPSHOT + 3.2.3 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index edd7ddc30..1626a52dc 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.3-SNAPSHOT + 3.2.3 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index d4cc2c087..ce2f8f94b 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 453c507ab..42673605d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index a4a793901..fef77ae53 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 ../pom.xml diff --git a/pom.xml b/pom.xml index e86b2fa37..c3e9fd852 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 3.2.3-SNAPSHOT + 3.2.3 pom onebusaway-gtfs-modules @@ -39,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v3.2.3 From 410a7cb9611757491b57e5db43e58242e6ba2d8e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:54:34 +0200 Subject: [PATCH 126/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 008583644..2edd28f21 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 81fb228ed..5702a6e29 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index d2f7a8d9d..0ec0b513e 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 33830de0c..928f75d2f 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index c81c4bb5f..9be10868f 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.3 + 3.2.4-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 1626a52dc..bdd6c2bad 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.3 + 3.2.4-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index ce2f8f94b..561187e4b 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 42673605d..dd0c98c5d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index fef77ae53..7af22d190 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index c3e9fd852..8c7184b33 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.3 + 3.2.4-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v3.2.3 + HEAD From d3c2a8f1dc9536eb604d95def8551495913c5b9a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 10:57:34 +0200 Subject: [PATCH 127/234] Update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a6f0c1ea0..ec2affdc7 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ In your `pom.xml`, include: ``` - org.onebusaway - onebusaway-gtfs - 2.0.0 + org.onebusaway + onebusaway-gtfs + ${VERSION} ``` From bc3558716ad43c5c6bd2cb106cc3ce5b51597bfc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Sep 2024 11:13:27 +0200 Subject: [PATCH 128/234] Remove build variables --- onebusaway-gtfs-merge-cli/pom.xml | 6 ------ onebusaway-gtfs-transformer-cli/pom.xml | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 9be10868f..b38729bd5 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -10,12 +10,6 @@ onebusaway-gtfs-merge-cli Command-line interface to the GTFS merge tool. - - - false - - org.onebusaway diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 561187e4b..308ca78ba 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -61,20 +61,8 @@ org.apache.maven.plugins maven-deploy-plugin - - ${skip-deploy-onebusaway-gtfs-transformer-cli-jar} - - - - oss-distribution - - true - - - - From a12648902c5ce4fdeeead33a8ff92dbd185c4d86 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Sep 2024 13:51:54 +0200 Subject: [PATCH 129/234] Add note on Maven Central --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ec2affdc7..b1e9a4044 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,9 @@ In your `pom.xml`, include: ``` ...where `` contains the latest version number. + +## Update on camsys-apps.com repo + +In August 2024 @leonardehrenfried took over maintainership and subsequently all artifacts are +now again published to Maven Central. Adding camsys-apps.com to your Maven repo configuration is no +longer necessary when you use version 1.4.18 or newer. \ No newline at end of file From 01cef91043b30574aa74bbb25d09640deb0e4444 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Sep 2024 11:27:50 +0200 Subject: [PATCH 130/234] Add experimental tag to flex fields --- .../src/main/java/org/onebusaway/gtfs/model/Trip.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index 843103055..8881dd26c 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -78,15 +78,19 @@ public final class Trip extends IdentityBean { @CsvField(optional = true) private String continuousDropOffMessage; + @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double meanDurationFactor; + @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double meanDurationOffset; + @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double safeDurationFactor; + @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double safeDurationOffset; From a1270425ac2ef325782a91e53605ae879db68742 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Sep 2024 23:59:43 +0200 Subject: [PATCH 131/234] Remove library com.kurtraschke:wsf-api --- onebusaway-gtfs-transformer/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index dd0c98c5d..c2ad64102 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -55,11 +55,6 @@ mockito-core test - - com.kurtraschke - wsf-api - 1.1 - org.onebusaway onebusaway-cloud-api From f0c00953060f6da7daf778bae57a0c1a1dfd435e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 16 Sep 2024 11:22:05 +0200 Subject: [PATCH 132/234] Remove WSFBlockResolutionStrategy --- .../impl/WSFBlockResolutionStrategy.java | 396 ------------------ 1 file changed, 396 deletions(-) delete mode 100644 onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/WSFBlockResolutionStrategy.java diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/WSFBlockResolutionStrategy.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/WSFBlockResolutionStrategy.java deleted file mode 100644 index fce5667fb..000000000 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/WSFBlockResolutionStrategy.java +++ /dev/null @@ -1,396 +0,0 @@ -/** - * Copyright (C) 2016 Cambridge Systematics, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onebusaway.gtfs_transformer.impl; - -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.transform.stream.StreamSource; - -import org.onebusaway.csv_entities.schema.annotations.CsvField; -import org.onebusaway.gtfs.impl.calendar.CalendarServiceDataFactoryImpl; -import org.onebusaway.gtfs.model.Agency; -import org.onebusaway.gtfs.model.AgencyAndId; -import org.onebusaway.gtfs.model.Route; -import org.onebusaway.gtfs.model.ServiceCalendar; -import org.onebusaway.gtfs.model.StopTime; -import org.onebusaway.gtfs.model.StopLocation; -import org.onebusaway.gtfs.model.Trip; -import org.onebusaway.gtfs.model.calendar.CalendarServiceData; -import org.onebusaway.gtfs.model.calendar.ServiceDate; -import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; -import org.onebusaway.gtfs.services.GtfsRelationalDao; -import org.onebusaway.gtfs.services.calendar.CalendarServiceDataFactory; -import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; -import org.onebusaway.gtfs_transformer.services.TransformContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import gov.wa.wsdot.ferries.schedule.SchedResponse; -import gov.wa.wsdot.ferries.schedule.SchedTerminalCombo; -import gov.wa.wsdot.ferries.schedule.SchedTime; - -public class WSFBlockResolutionStrategy implements GtfsTransformStrategy { - - private static final Logger _log = LoggerFactory.getLogger( - WSFBlockResolutionStrategy.class); - - @CsvField(ignore=true) - private GtfsMutableRelationalDao _dao; - - @CsvField(ignore=true) - private String _agencyId; - - @CsvField(ignore=true) - private TimeZone _agencyTimeZone; - - @CsvField(ignore=true) - private WSFTripResolutionService _tripResolutionService; - - @CsvField(ignore=true) - private WSFScheduleService _scheduleService; - - private String apiAccessCode = ""; - - @Override - public String getName() { - return this.getClass().getSimpleName(); - } - - @Override - public void run(TransformContext context, GtfsMutableRelationalDao dao) { - _dao = dao; - - Agency agency = dao.getAllAgencies().iterator().next(); - _agencyId = agency.getId(); - _agencyTimeZone = TimeZone.getTimeZone(agency.getTimezone()); - - _tripResolutionService = new WSFTripResolutionService(_dao, _agencyId, - _agencyTimeZone); - - try { - _scheduleService = new WSFScheduleService(apiAccessCode); - setAllBlockIds(); - } catch (Exception e) { - _log.error("Error initializing WSFBlockResolutionStrategy: " + e); - return; - } - - } - - public void setApiAccessCode(String apiAccessCode) { - this.apiAccessCode = apiAccessCode; - } - - private void setAllBlockIds() throws InterruptedException { - Collection trips = _dao.getAllTrips(); - - Map tripsMap = new HashMap(); - for (Trip t : trips) { - - BlockTask task = new BlockTask(t); - - // No data for trips in past - if (task.isInPast()) { - continue; - } - - if (!tripsMap.containsKey(task)) { - tripsMap.put(task, task); - } - task = tripsMap.get(task); - - task.addTrip(t); - } - - Set jobs = tripsMap.keySet(); - - // Oversubscribe cores since we are network-bound - int nCores = Runtime.getRuntime().availableProcessors(); - ExecutorService executor = Executors.newFixedThreadPool(nCores * 2); - - executor.invokeAll(jobs); - - } - - private void setBlockIdsFromSchedResponse(SchedResponse resp) { - List combos = resp.getTerminalCombos().getValue().getSchedTerminalCombo(); - for (SchedTerminalCombo stc : combos) { - - String depart = stc.getDepartingTerminalID().toString(); - String arrive = stc.getArrivingTerminalID().toString(); - - for (SchedTime sched : schedTime(stc)) { - long time = ts(sched.getDepartingTime()); - Trip trip = _tripResolutionService.resolve(depart, time, arrive); - if (trip != null) { - trip.setBlockId(sched.getVesselID().toString()); - } else { - _log.warn("Skipping schedTime due to no matching trip {}", - sched.toString()); - } - } - } - } - - private String id(StopLocation st) { - return st.getId().getId(); - } - - private Date date(ServiceCalendar cal) { - return cal.getStartDate().getAsDate(_agencyTimeZone); - } - - private List schedTime(SchedTerminalCombo st) { - return st.getTimes().getValue().getSchedTime(); - } - - // From WSFRealtimeProvider - private long ts(XMLGregorianCalendar xgc) { - GregorianCalendar gc = xgc.toGregorianCalendar(_agencyTimeZone, null, null); - return (gc.getTimeInMillis() / 1000L); - } - - /** - * We need to query the API for each service date, for each stop pair (route). - * This class allows us to construct an index of trips that have the same - * service date and route. In addition, it is a Callable so that tasks can be - * run asynchronously. - * - */ - class BlockTask implements Callable { - Route route; - ServiceCalendar cal; - List trips; - - BlockTask(Trip t) { - this.route = t.getRoute(); - this.cal = _dao.getCalendarForServiceId(t.getServiceId()); - } - - @Override - public Boolean call() { - List stops = _dao.getStopTimesForTrip(trips.get(0)); - StopLocation orig = stops.get(0).getStop(), dest = stops.get(1).getStop(); - - _log.info("Submitting WSF block task for {} ({}, {})", cal, orig, dest); - SchedResponse resp = _scheduleService.getSchedule(date(cal), id(orig), - id(dest)); - - setBlockIdsFromSchedResponse(resp); - - return true; - } - - void addTrip(Trip t) { - if (trips == null) { - trips = new ArrayList(); - } - trips.add(t); - } - - boolean isInPast() { - Calendar today = Calendar.getInstance(); - today.set(Calendar.HOUR_OF_DAY, 0); - today.set(Calendar.MINUTE, 0); - today.set(Calendar.SECOND, 0); - today.set(Calendar.MILLISECOND, 0); - - return date(cal).before(today.getTime()); - } - - @Override - public int hashCode() { - return route.hashCode() * 17 + cal.getServiceId().hashCode() * 31; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof BlockTask)) - return false; - BlockTask k = (BlockTask) o; - if (!this.route.equals(k.route)) - return false; - if (!this.cal.equals(k.cal)) - return false; - - return true; - } - } - -} - -/** - * Adapted from TripResolutionService in wsf-gtfsrealtime - */ -class WSFTripResolutionService { - - private static final Logger _log = LoggerFactory.getLogger( - WSFTripResolutionService.class); - - GtfsRelationalDao _dao; - String _agencyId; - CalendarServiceData _csd; - TimeZone _agencyTimeZone; - - int _maxStopTime; - - public WSFTripResolutionService(GtfsRelationalDao dao, String agencyId, - TimeZone agencyTimeZone) { - _dao = dao; - _agencyId = agencyId; - _agencyTimeZone = agencyTimeZone; - - CalendarServiceDataFactory factory = new CalendarServiceDataFactoryImpl( - _dao); - _csd = factory.createData(); - - _maxStopTime = calculateMaxStopTime(); - } - - public Trip resolve(String departingTerminalId, long departureTime, - String arrivingTerminalId) { - ServiceDate initialServiceDate = new ServiceDate( - new Date(departureTime * 1000)); - int lookBackDays = (_maxStopTime / 86400) + 1; - - AgencyAndId stopId = new AgencyAndId(_agencyId, departingTerminalId); - AgencyAndId routeId = new AgencyAndId(_agencyId, - departingTerminalId + arrivingTerminalId); - - for (StopTime st : _dao.getAllStopTimes()) { - if (st.getStop().getId().equals(stopId) - && st.getTrip().getRoute().getId().equals(routeId)) { - - ServiceDate sd = initialServiceDate; - for (int i = 0; i < lookBackDays; i++) { - - if (_csd.getServiceIdsForDate(sd).contains( - st.getTrip().getServiceId()) - && st.getDepartureTime() == (departureTime - - (sd.getAsCalendar(_agencyTimeZone).getTimeInMillis() - / 1000))) { - - return st.getTrip(); - } - - sd = sd.previous(); - } - - } - } - - _log.warn("no trip found for resolve(departId=" + departingTerminalId - + ", departureTime=" + departureTime + ", arrivalId=" - + arrivingTerminalId + ")"); - return null; - - } - - private int calculateMaxStopTime() { - Set times = new HashSet(); - - for (StopTime st : _dao.getAllStopTimes()) { - if (st.isArrivalTimeSet()) { - times.add(st.getArrivalTime()); - } - if (st.isDepartureTimeSet()) { - times.add(st.getDepartureTime()); - } - } - - return Collections.max(times); - } -} - -/** - * Query the WSF API for a schedule response. - */ -class WSFScheduleService { - - private static Logger _log = LoggerFactory.getLogger( - WSFScheduleService.class); - - private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat( - "yyyy-MM-dd"); - - private String _apiAccessCode; - - // Context is thread-safe. Unmarshaller is not. - JAXBContext _jc; - - public WSFScheduleService(String apiAccessCode) throws JAXBException { - _jc = JAXBContext.newInstance(SchedResponse.class); - _apiAccessCode = apiAccessCode; - } - - public SchedResponse getSchedule(Date serviceDate, String departTerminal, - String arriveTerminal) { - long start = System.currentTimeMillis(); - StringBuffer url = new StringBuffer( - "https://www.wsdot.wa.gov/ferries/api/schedule/rest/schedule"); - url.append("/" + formatDate(serviceDate)); - url.append("/" + departTerminal); - url.append("/" + arriveTerminal); - url.append("?apiaccesscode=" + _apiAccessCode); - - try { - URLConnection conn = new URL(url.toString()).openConnection(); - conn.setRequestProperty("Accept", "text/xml"); - - InputStream is = conn.getInputStream(); - try { - JAXBElement resp = _jc.createUnmarshaller().unmarshal( - new StreamSource(is), SchedResponse.class); - - return resp.getValue(); - } finally { - is.close(); - long finish = System.currentTimeMillis(); - _log.info("wsf call complete in " + (finish-start)/1000 + "s for call=" + url); - } - } catch (Exception e) { - _log.error("Exception processing WSF API for api call:'" + url.toString() + "', " + e); - return null; - } - - } - - private static String formatDate(Date date) { - return DATE_FORMATTER.format(date); - } -} \ No newline at end of file From d5f103f0c37d582137104e707d497a062510a1fd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 16 Sep 2024 11:55:51 +0200 Subject: [PATCH 133/234] Add lable to renovate --- renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index 14f72cb21..4bca9efcb 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -5,6 +5,9 @@ ], "prConcurrentLimit": 3, "rebaseWhen": "conflicted", + "labels": [ + "dependencies" + ], "packageRules": [ // some dependencies that we auto-merge release very often and even the auto-merges create a lot of // noise, so we slow it down a bit From 80c29d9a3705f6dc3255846da8b108cc22de737c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Sep 2024 07:24:21 +0200 Subject: [PATCH 134/234] Add auto merge rule --- renovate.json5 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index 4bca9efcb..b191d3a7c 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -47,6 +47,14 @@ ], "automerge": true, "schedule": "on the 4th day of the month" + }, + { + "description": "Automerge dependencies", + "matchPackageNames": [ + "io.github.classgraph:classgraph", + ], + "automerge": true, + "schedule": "after 1am every weekday" } ], "timezone": "Europe/Berlin" From eb705ec5810a7468357fa064a235e0bd063348ec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:21:23 +0000 Subject: [PATCH 135/234] Update dependency io.github.classgraph:classgraph to v4.8.176 --- onebusaway-collections/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 2edd28f21..c705d9d44 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -24,7 +24,7 @@ io.github.classgraph classgraph - 4.8.175 + 4.8.176 From 407c2eb20d3dde3ff9b91a9e7872f9b2ea8788cf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Sep 2024 10:00:52 +0200 Subject: [PATCH 136/234] Update docs --- docs/index.md | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/index.md b/docs/index.md index 3889defd7..f30d33572 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,21 +8,17 @@ * `onebusaway-gtfs-hibernate` - Support for [Hibernate](http://www.hibernate.org/) database persistence of GTFS data - * `onebusaway-gtfs-hibernate-cli` - Command-line utilty for loading GTFS feeds into a database - see [the full documentation](./onebusaway-gtfs-hibernate-cli.html). + * `onebusaway-gtfs-hibernate-cli` - Command-line utilty for loading GTFS feeds into a database - see [the full documentation](./onebusaway-gtfs-hibernate-cli.md). * `onebusaway-gtfs-transformer` - Tools for transforming GTFS data - * `onebusaway-gtfs-transformer-cli` - Command-line utility for transforming GTFS - see [the full documentation](./onebusaway-gtfs-transformer-cli.html). + * `onebusaway-gtfs-transformer-cli` - Command-line utility for transforming GTFS - see [the full documentation](./onebusaway-gtfs-transformer-cli.md). * `onebusaway-gtfs-merge` - Tools for merging GTFS data - * `onebusaway-gtfs-merge-cli` - Command-line utility for merging GTFS feeds - see [the full documentation](./onebusaway-gtfs-merge-cli.html). + * `onebusaway-gtfs-merge-cli` - Command-line utility for merging GTFS feeds - see [the full documentation](./onebusaway-gtfs-merge-cli.md). -Documentation - - You can access the [latest Javadoc for the library](./apidocs/index.html). Also, see example source code below. - -Using in Maven +## Using in Maven The library is available as a Maven module. Simply add the following dependencies: @@ -49,11 +45,7 @@ Using in Maven ``` -#if( $currentVersion.contains('SNAPSHOT') ) - To use a SNAPSHOT version of the library, you'll need to [add a reference to the OneBusAway Maven repository](https://github.com/OneBusAway/onebusaway/wiki/Maven-Repository). -#end - -Example Code +## Example Code * Basic Reading From 6da8a665866b0e3a0ace45747836d5ddb39357c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 22:55:20 +0000 Subject: [PATCH 137/234] Update dependency org.apache.maven.plugins:maven-gpg-plugin to v3.2.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8c7184b33..1a7393038 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.6 sign-artifacts From f4a8fa278b1475b89ec6bc736f235614cc3f11e9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 23 Sep 2024 12:04:36 +0200 Subject: [PATCH 138/234] Configure automatic changelog --- .github/release.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..720e9c53d --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,8 @@ +changelog: + categories: + - title: Breaking changes + labels: + - "Breaking change" + - title: Dependency upgrades + labels: + - dependencies From afde844bd7981e57abfc5cc8ed68554afaf29d01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:41:15 +0000 Subject: [PATCH 139/234] Update dependency org.sonatype.central:central-publishing-maven-plugin to v0.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a7393038..d161f3134 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.5.0 + 0.6.0 true central From 39baac357358ba5c081f3c097bafc4189e2f3ebc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 07:02:36 +0200 Subject: [PATCH 140/234] Add full integration test for transformer-cli --- cli-tests/cli-tests.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 cli-tests/cli-tests.sh diff --git a/cli-tests/cli-tests.sh b/cli-tests/cli-tests.sh new file mode 100755 index 000000000..0ba02e509 --- /dev/null +++ b/cli-tests/cli-tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +mvn clean package -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true + +# transformer-cli + +cp onebusaway-gtfs-transformer-cli/target/onebusaway-gtfs-transformer-cli.jar ./transformer-cli.jar +wget https://github.com/google/transit/blob/master/gtfs/spec/en/examples/sample-feed-1.zip?raw=true -O gtfs.zip + +java -jar transformer-cli.jar --help + +java -jar transformer-cli.jar --transform="{'op':'remove','match':{'file':'stops.txt','stop_id':'BEATTY_AIRPORT'}}" gtfs.zip gtfs.transformed.zip From 1ba2a08400dc73cc9879fceed9e27dd67d6bccdd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 07:03:26 +0200 Subject: [PATCH 141/234] Add zip, jar to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fcc619fa9..fd7bee41a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ pom.xml.releaseBackup /pom.xml.versionsBackup *.iml +*.zip +*.jar From b7f6089a94900ab667f53a7bfedc50ca1d288c50 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 07:06:42 +0200 Subject: [PATCH 142/234] Run CLI tests on GH actions --- .github/workflows/ci.yml | 18 +++++++++++++++++- cli-tests/cli-tests.sh | 10 ++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b30e6de87..bb92cbb0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,4 +42,20 @@ jobs: cache: 'maven' - name: Test project with Maven - run: mvn --no-transfer-progress test package \ No newline at end of file + run: mvn --no-transfer-progress test package + + cli: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'maven' + + - name: Run CLI e2e tests + run: ./cli-tests/cli-tests.sh diff --git a/cli-tests/cli-tests.sh b/cli-tests/cli-tests.sh index 0ba02e509..7fccf0fa2 100755 --- a/cli-tests/cli-tests.sh +++ b/cli-tests/cli-tests.sh @@ -1,12 +1,14 @@ #!/bin/bash -mvn clean package -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true +mvn clean package --no-transfer-progress -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true # transformer-cli -cp onebusaway-gtfs-transformer-cli/target/onebusaway-gtfs-transformer-cli.jar ./transformer-cli.jar +TRANSFORMER_JAR="transformer-cli.jar" + +cp onebusaway-gtfs-transformer-cli/target/onebusaway-gtfs-transformer-cli.jar ./${TRANSFORMER_JAR} wget https://github.com/google/transit/blob/master/gtfs/spec/en/examples/sample-feed-1.zip?raw=true -O gtfs.zip -java -jar transformer-cli.jar --help +java -jar ${TRANSFORMER_JAR} --help -java -jar transformer-cli.jar --transform="{'op':'remove','match':{'file':'stops.txt','stop_id':'BEATTY_AIRPORT'}}" gtfs.zip gtfs.transformed.zip +java -jar ${TRANSFORMER_JAR} --transform="{'op':'remove','match':{'file':'stops.txt','stop_id':'BEATTY_AIRPORT'}}" gtfs.zip gtfs.transformed.zip From 113638fa020b03599f3ff6a22ce7467ea42eb4e9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 07:11:48 +0200 Subject: [PATCH 143/234] Revert "Update dependency commons-cli:commons-cli to v1.9.0" This reverts commit 5a2e50f69adbcdeed1f4f9ee05936499130521ae. --- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 0ec0b513e..8ad11aa10 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -19,7 +19,7 @@ commons-cli commons-cli - 1.9.0 + 1.2 org.slf4j diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index b38729bd5..b0b5221b5 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -19,7 +19,7 @@ commons-cli commons-cli - 1.9.0 + 1.2 org.slf4j diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 308ca78ba..ac367dcf4 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -21,7 +21,7 @@ commons-cli commons-cli - 1.9.0 + 1.2 org.slf4j From 5b1b5625be95fffa1aa3351dd984c089a0c844fb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:04:02 +0200 Subject: [PATCH 144/234] Configure bugfixes in release notes --- .github/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/release.yml b/.github/release.yml index 720e9c53d..85bf4d649 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -3,6 +3,9 @@ changelog: - title: Breaking changes labels: - "Breaking change" + - title: Bugfixes + labels: + - bug - title: Dependency upgrades labels: - dependencies From 6921428643f54f6a91c0d60ebbae2133865b6873 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:09:24 +0200 Subject: [PATCH 145/234] [maven-release-plugin] prepare release v3.2.4 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index c705d9d44..f1a9da6a2 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 5702a6e29..c30a623c0 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 8ad11aa10..da1afe1dd 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 928f75d2f..15d5ff7b8 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index b0b5221b5..6ccd8c9b8 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.4-SNAPSHOT + 3.2.4 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index bdd6c2bad..385e9fe20 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.4-SNAPSHOT + 3.2.4 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index ac367dcf4..c14231966 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index dd0c98c5d..1f7a833e1 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 7af22d190..5bbe263b5 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 ../pom.xml diff --git a/pom.xml b/pom.xml index 1a7393038..d6c6794fd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4-SNAPSHOT + 3.2.4 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v3.2.4 From eb5df6166bb0b2aca1d261e1f73049d647835edc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:09:25 +0200 Subject: [PATCH 146/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index f1a9da6a2..a7cc8e69c 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index c30a623c0..4f874be55 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index da1afe1dd..6160e8578 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 15d5ff7b8..aa0e42a49 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 6ccd8c9b8..b69646721 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.4 + 3.2.5-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 385e9fe20..b8f9d42dd 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.4 + 3.2.5-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index c14231966..57d18a8bc 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 1f7a833e1..152e08e3d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 5bbe263b5..bb49a0575 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index d6c6794fd..b06ab4e4a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.4 + 3.2.5-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v3.2.4 + HEAD From b64707148cfa4061b98913e4f09c0e51bac0db74 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:26:12 +0200 Subject: [PATCH 147/234] Refactor CLI parsing --- .../gtfs_transformer/GtfsTransformerMain.java | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java index 0ea998428..be41ff048 100644 --- a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java +++ b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java @@ -20,8 +20,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -45,7 +45,7 @@ public class GtfsTransformerMain { - private static Logger _log = LoggerFactory.getLogger(GtfsTransformerMain.class); + private static final Logger LOG = LoggerFactory.getLogger(GtfsTransformerMain.class); /**** * Generic Arguments @@ -177,11 +177,11 @@ protected void buildOptions(Options options) { "overwrite duplicate elements"); } - protected void printHelp(PrintWriter out, Options options) throws IOException { + private void printHelp() throws IOException { InputStream is = getClass().getResourceAsStream("usage.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - String line = null; + String line; while ((line = reader.readLine()) != null) { System.err.println(line); @@ -193,22 +193,23 @@ protected void printHelp(PrintWriter out, Options options) throws IOException { protected void runApplication(CommandLine cli, String[] originalArgs) throws Exception { - String[] args = cli.getArgs(); + var args = Arrays.stream(cli.getArgs()).toList(); - if (args.length < 2) { + if (args.size() < 2) { printHelp(); System.exit(-1); } - List paths = new ArrayList(); - for (int i = 0; i < args.length - 1; ++i) { - paths.add(new File(args[i])); - _log.info("input path: " + args[i]); - } + List inputPaths = args.stream().limit(args.size() - 1).map(File::new + ).toList(); + LOG.info("input paths: {}", inputPaths); + GtfsTransformer transformer = new GtfsTransformer(); - transformer.setGtfsInputDirectories(paths); - transformer.setOutputDirectory(new File(args[args.length - 1])); - _log.info("output path: " + args[args.length - 1]); + transformer.setGtfsInputDirectories(inputPaths); + + var outputPath = new File(args.getLast()); + transformer.setOutputDirectory(outputPath); + LOG.info("output path: {}", outputPath); Option[] options = getOptionsInCommandLineOrder(cli, originalArgs); @@ -371,14 +372,6 @@ private void configureVerifyRoutesFile(GtfsTransformer updater, String file) { updater.addParameter("verifyRoutesFile", file); } - /***************************************************************************** - * Protected Methods - ****************************************************************************/ - - protected void printHelp() throws IOException { - printHelp(new PrintWriter(System.err, true), _options); - } - private boolean needsHelp(String[] args) { for (String arg : args) { if (arg.equals("-h") || arg.equals("--help") || arg.equals("-help")) From 4ecc5f10947f1e9cb71f224ba155887ca642b82c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:38:29 +0200 Subject: [PATCH 148/234] Finish clean up --- .../gtfs_transformer/GtfsTransformerMain.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java index be41ff048..ceff209e7 100644 --- a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java +++ b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java @@ -21,7 +21,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -87,9 +86,9 @@ public class GtfsTransformerMain { private static final String ARG_OVERWRITE_DUPLICATES = "overwriteDuplicates"; - private static CommandLineParser _parser = new PosixParser(); + private static final CommandLineParser parser = new PosixParser(); - private Options _options = new Options(); + private final Options options = new Options(); public static void main(String[] args) throws IOException { GtfsTransformerMain m = new GtfsTransformerMain(); @@ -98,7 +97,7 @@ public static void main(String[] args) throws IOException { public GtfsTransformerMain() { - buildOptions(_options); + buildOptions(options); } /***************************************************************************** @@ -113,13 +112,9 @@ public void run(String[] args) throws IOException { } try { - CommandLine cli = _parser.parse(_options, args, true); + CommandLine cli = parser.parse(options, args, true); runApplication(cli, args); - } catch (MissingOptionException ex) { - System.err.println("Missing argument: " + ex.getMessage()); - printHelp(); - System.exit(-2); - } catch (MissingArgumentException ex) { + } catch (MissingOptionException | MissingArgumentException ex) { System.err.println("Missing argument: " + ex.getMessage()); printHelp(); System.exit(-2); @@ -193,7 +188,7 @@ private void printHelp() throws IOException { protected void runApplication(CommandLine cli, String[] originalArgs) throws Exception { - var args = Arrays.stream(cli.getArgs()).toList(); + var args = cli.getArgList(); if (args.size() < 2) { printHelp(); @@ -382,9 +377,9 @@ private boolean needsHelp(String[] args) { private static class Ordered implements Comparable> { - private T _object; + private final T _object; - private int _order; + private final int _order; public Ordered(T object, int order) { _object = object; From 5ad9cc39b7134ce8cd6aa9faab8f83daf91bbd0f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:42:11 +0200 Subject: [PATCH 149/234] Fix conversion of array --- cli-tests/cli-tests.sh | 2 +- .../onebusaway/gtfs_transformer/GtfsTransformerMain.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli-tests/cli-tests.sh b/cli-tests/cli-tests.sh index 7fccf0fa2..0667a6827 100755 --- a/cli-tests/cli-tests.sh +++ b/cli-tests/cli-tests.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash +x mvn clean package --no-transfer-progress -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true diff --git a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java index ceff209e7..673cc20eb 100644 --- a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java +++ b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -188,15 +189,14 @@ private void printHelp() throws IOException { protected void runApplication(CommandLine cli, String[] originalArgs) throws Exception { - var args = cli.getArgList(); + var args = Arrays.stream(cli.getArgs()).toList(); if (args.size() < 2) { printHelp(); System.exit(-1); } - List inputPaths = args.stream().limit(args.size() - 1).map(File::new - ).toList(); + List inputPaths = args.stream().limit(args.size() - 1).map(File::new).toList(); LOG.info("input paths: {}", inputPaths); GtfsTransformer transformer = new GtfsTransformer(); From d3c2137525006eddf1fdde80b1db58c6ea5210f2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 24 Sep 2024 08:47:00 +0200 Subject: [PATCH 150/234] Use Java 17 style --- .../org/onebusaway/gtfs_transformer/GtfsTransformerMain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java index 673cc20eb..a1e107933 100644 --- a/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java +++ b/onebusaway-gtfs-transformer-cli/src/main/java/org/onebusaway/gtfs_transformer/GtfsTransformerMain.java @@ -202,7 +202,7 @@ protected void runApplication(CommandLine cli, String[] originalArgs) GtfsTransformer transformer = new GtfsTransformer(); transformer.setGtfsInputDirectories(inputPaths); - var outputPath = new File(args.getLast()); + var outputPath = new File(args.get(args.size() - 1)); transformer.setOutputDirectory(outputPath); LOG.info("output path: {}", outputPath); From 85f9d72a7bfcb1c5f209f8a097fd6c054725afef Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 25 Sep 2024 12:19:32 +0200 Subject: [PATCH 151/234] Update PR template --- .github/PULL_REQUEST_TEMPLATE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9441be107..665ecfdf9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,5 +8,4 @@ Explain how you expect the pull request to work in your testing (in case other p Please make sure these boxes are checked before submitting your pull request - thanks! -- [ ] Format the title like "Fix # - short description of fix and changes" - [ ] Linked all relevant issues From 5b3cc7d41c0fd1f51a4a7ef43aaa81f2e868d9c7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 25 Sep 2024 12:20:27 +0200 Subject: [PATCH 152/234] Add e2e test for merge-cli --- cli-tests/cli-tests.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cli-tests/cli-tests.sh b/cli-tests/cli-tests.sh index 0667a6827..b3d56200d 100755 --- a/cli-tests/cli-tests.sh +++ b/cli-tests/cli-tests.sh @@ -2,13 +2,27 @@ mvn clean package --no-transfer-progress -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true +EXAMPLE_1="example.gtfs.zip" +EXAMPLE_2="deathvalley.gtfs.zip" + # transformer-cli TRANSFORMER_JAR="transformer-cli.jar" cp onebusaway-gtfs-transformer-cli/target/onebusaway-gtfs-transformer-cli.jar ./${TRANSFORMER_JAR} -wget https://github.com/google/transit/blob/master/gtfs/spec/en/examples/sample-feed-1.zip?raw=true -O gtfs.zip +wget https://github.com/google/transit/blob/master/gtfs/spec/en/examples/sample-feed-1.zip?raw=true -O ${EXAMPLE_1} java -jar ${TRANSFORMER_JAR} --help -java -jar ${TRANSFORMER_JAR} --transform="{'op':'remove','match':{'file':'stops.txt','stop_id':'BEATTY_AIRPORT'}}" gtfs.zip gtfs.transformed.zip +java -jar ${TRANSFORMER_JAR} --transform="{'op':'remove','match':{'file':'stops.txt','stop_id':'BEATTY_AIRPORT'}}" ${EXAMPLE_1} gtfs.transformed.zip + +# merge-cli + +MERGE_JAR="merge-cli.jar" + +cp onebusaway-gtfs-merge-cli/target/onebusaway-gtfs-merge-cli-*.jar ./${MERGE_JAR} +wget "http://data.trilliumtransit.com/gtfs/deathvalley-demo-ca-us/deathvalley-demo-ca-us.zip" -O ${EXAMPLE_2} + +java -jar ${MERGE_JAR} --help + +java -jar ${MERGE_JAR} ${EXAMPLE_1} ${EXAMPLE_2} gtfs.merged.zip From 204310c351bf78014c0c27261e205254fa75cdce Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 25 Sep 2024 12:24:37 +0200 Subject: [PATCH 153/234] Rename CI jobs and steps --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb92cbb0b..e1d2e8b66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: branches: [ master ] jobs: - build: + unit-tests: runs-on: ubuntu-latest @@ -44,7 +44,7 @@ jobs: - name: Test project with Maven run: mvn --no-transfer-progress test package - cli: + cli-integration-tests: runs-on: ubuntu-latest @@ -57,5 +57,5 @@ jobs: java-version: '17' cache: 'maven' - - name: Run CLI e2e tests + - name: Run CLI integration tests run: ./cli-tests/cli-tests.sh From 8ff493197d8fa687686898a7a887890355bc5dd1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 25 Sep 2024 12:27:15 +0200 Subject: [PATCH 154/234] Format documentation --- docs/onebusaway-gtfs-merge-cli.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/onebusaway-gtfs-merge-cli.md b/docs/onebusaway-gtfs-merge-cli.md index 9d5038b97..5c8fbb3c2 100644 --- a/docs/onebusaway-gtfs-merge-cli.md +++ b/docs/onebusaway-gtfs-merge-cli.md @@ -15,19 +15,21 @@ https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-merge-cli/3.2.2/on ## Using the Application -You'll need a Java 17 runtime installed to run the client. To run the application: +You'll need a Java 17 runtime installed to run the client. + +To run the application: ``` java -jar onebusaway-gtfs-merge-cli.jar [--args] input_gtfs_path_a input_gtfs_path_b ... output_gtfs_path ``` - <>: Merging large GTFS feeds is often processor and memory intensive. You'll likely need to increase the +**Note**: Merging large GTFS feeds is often processor and memory intensive. You'll likely need to increase the max amount of memory allocated to Java with an option like `-Xmx1G` (adjust the limit as needed). I also recommend adding the `-server` argument if you are running the Oracle or OpenJDK, as it can really increase performance. -Configuring the Application +## Configuring the Application - The merge application supports a number of options and arguments for configuring the application's behavior. The +The merge application supports a number of options and arguments for configuring the application's behavior. The general pattern is to specify options for each type of file in a GTFS feed using the `--file` option, specifying specific options for each file type after the `--file` option. Here's a quick example: From 09e52e2f9a3f1d134d53bf57e7e0ec52a2b24d2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 03:27:56 +0000 Subject: [PATCH 155/234] Update dependency com.fasterxml.jackson.core:jackson-databind to v2.18.0 --- onebusaway-gtfs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index bb49a0575..eb07f096c 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -26,7 +26,7 @@ com.fasterxml.jackson.core jackson-databind - 2.17.2 + 2.18.0 de.grundid.opendatalab From 7a3c2025edd1f2b8493e67290a88660cf0de0e7d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 03:28:59 +0000 Subject: [PATCH 156/234] Update dependency io.github.classgraph:classgraph to v4.8.177 --- onebusaway-collections/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index a7cc8e69c..726f277fa 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -24,7 +24,7 @@ io.github.classgraph classgraph - 4.8.176 + 4.8.177 From 7d90a2b241283c45dc5d09ea943c6e236e9dad15 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 2 Oct 2024 09:42:57 +0200 Subject: [PATCH 157/234] Monthly dependendency upgrades --- renovate.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renovate.json5 b/renovate.json5 index b191d3a7c..3688ef81d 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -54,7 +54,7 @@ "io.github.classgraph:classgraph", ], "automerge": true, - "schedule": "after 1am every weekday" + "schedule": "monthly" } ], "timezone": "Europe/Berlin" From 447e8c152b81f8e6cca7ef9efcb5528ce2a36df3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:49:34 +0000 Subject: [PATCH 158/234] Update dependency com.mysql:mysql-connector-j to v9.1.0 --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index aa0e42a49..10fee17be 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -35,7 +35,7 @@ com.mysql mysql-connector-j - 9.0.0 + 9.1.0 org.slf4j From 557d1d9ff14fe372d8f0a5fda1658364b9cf9876 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:29:56 +0000 Subject: [PATCH 159/234] Update Test dependencies --- onebusaway-csv-entities/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 4f874be55..e9242e61c 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -35,7 +35,7 @@ org.mockito mockito-core - 5.13.0 + 5.14.2 test diff --git a/pom.xml b/pom.xml index 4299de8a5..76f6d6eeb 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ UTF-8 2.0.16 0.0.13 - 5.11.0 + 5.11.2 @@ -90,7 +90,7 @@ org.mockito mockito-core - 5.13.0 + 5.14.2 test From 0a4a24f00b62f5d04f66f09009d9b39be7c7ebb9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:30:45 +0000 Subject: [PATCH 160/234] Update Maven plugins --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 76f6d6eeb..1ea43940f 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.10.0 + 3.10.1 none false @@ -165,7 +165,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.6 + 3.2.7 sign-artifacts @@ -188,7 +188,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.0 + 3.5.1 me.fabriciorby From 654911c4d0258d0489f43a5ce93a17769cd78bb9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 02:09:54 +0000 Subject: [PATCH 161/234] Update dependency com.fasterxml.jackson.core:jackson-databind to v2.18.1 --- onebusaway-gtfs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index eb07f096c..3be86a28d 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -26,7 +26,7 @@ com.fasterxml.jackson.core jackson-databind - 2.18.0 + 2.18.1 de.grundid.opendatalab From 10dbe66f2848d0533bd253a32fe8a1cb52fdc7a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:00:51 +0000 Subject: [PATCH 162/234] Migrate config renovate.json5 --- renovate.json5 | 86 ++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index 3688ef81d..129499fbb 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -1,61 +1,57 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', ], - "prConcurrentLimit": 3, - "rebaseWhen": "conflicted", - "labels": [ - "dependencies" + prConcurrentLimit: 3, + rebaseWhen: 'conflicted', + labels: [ + 'dependencies', ], - "packageRules": [ - // some dependencies that we auto-merge release very often and even the auto-merges create a lot of - // noise, so we slow it down a bit + packageRules: [ { - "description": "Automerge test dependencies in a single PR", - "groupName": "Test dependencies", - "matchPackageNames": [ - "org.mockito:mockito-core", - "com.tngtech.archunit:archunit", - "org.apache.maven.plugins:maven-surefire-plugin", - "me.fabriciorby:maven-surefire-junit5-tree-reporter", - "com.google.truth:truth", - "org.jacoco:jacoco-maven-plugin", // coverage plugin - "org.apache.commons:commons-compress" // only used by tests + description: 'Automerge test dependencies in a single PR', + groupName: 'Test dependencies', + matchPackageNames: [ + 'org.mockito:mockito-core', + 'com.tngtech.archunit:archunit', + 'org.apache.maven.plugins:maven-surefire-plugin', + 'me.fabriciorby:maven-surefire-junit5-tree-reporter', + 'com.google.truth:truth', + 'org.jacoco:jacoco-maven-plugin', + 'org.apache.commons:commons-compress', + 'org.junit.jupiter:{/,}**', ], - "matchPackagePrefixes": [ - "org.junit.jupiter:", - ], - "automerge": true, - "schedule": "on the 17th day of the month" + automerge: true, + schedule: 'on the 17th day of the month', }, { - "description": "Automerge Maven plugins in a single PR", - "groupName": "Maven plugins", - "matchPackagePrefixes": [ - "org.apache.maven.plugins:" + description: 'Automerge Maven plugins in a single PR', + groupName: 'Maven plugins', + schedule: 'on the 23rd day of the month', + automerge: true, + matchPackageNames: [ + 'org.apache.maven.plugins:{/,}**', ], - "schedule": "on the 23rd day of the month", - "automerge": true }, { - "description": "Automerge logging dependencies in a single PR", - "groupName": "logging dependencies", - "matchPackagePrefixes": [ - "org.slf4j:", - "ch.qos.logback:" + description: 'Automerge logging dependencies in a single PR', + groupName: 'logging dependencies', + automerge: true, + schedule: 'on the 4th day of the month', + matchPackageNames: [ + 'org.slf4j:{/,}**', + 'ch.qos.logback:{/,}**', ], - "automerge": true, - "schedule": "on the 4th day of the month" }, { - "description": "Automerge dependencies", - "matchPackageNames": [ - "io.github.classgraph:classgraph", + description: 'Automerge dependencies', + matchPackageNames: [ + 'io.github.classgraph:classgraph', ], - "automerge": true, - "schedule": "monthly" - } + automerge: true, + schedule: 'monthly', + }, ], - "timezone": "Europe/Berlin" + timezone: 'Europe/Berlin', } From 1f9e5ffdcd1b7afaafdc7e9562f016d9d9372d18 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:28:31 +0100 Subject: [PATCH 163/234] Remove branding URL from Agency and Route --- .../main/java/org/onebusaway/gtfs/model/Agency.java | 12 ------------ .../main/java/org/onebusaway/gtfs/model/Route.java | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Agency.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Agency.java index 3dc77b781..9dc9127bf 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Agency.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Agency.java @@ -47,9 +47,6 @@ public final class Agency extends IdentityBean { @CsvField(optional = true) private String email; - @CsvField(optional = true) - private String brandingUrl; - public Agency() { } @@ -61,7 +58,6 @@ public Agency(Agency a) { this.timezone = a.timezone; this.lang = a.lang; this.phone = a.phone; - this.brandingUrl = a.brandingUrl; this.email = a.email; this.fareUrl = a.fareUrl; } @@ -130,14 +126,6 @@ public void setEmail(String email) { this.email = email; } - public String getBrandingUrl() { - return brandingUrl; - } - - public void setBrandingUrl(String brandingUrl) { - this.brandingUrl = brandingUrl; - } - public String toString() { return ""; } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java index b2451d6c9..d5c30bfcb 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java @@ -72,9 +72,6 @@ public final class Route extends IdentityBean { @CsvField(optional = true) private int sortOrder = MISSING_VALUE; - @CsvField(optional = true) - private String brandingUrl; - // Custom extension representing (bus) route accepts regional fare card. // That is it has a vending machine on board. @CsvField(optional = true, name = "regional_fare_card", defaultValue = "0") @@ -96,7 +93,6 @@ public Route(Route r) { this.textColor = r.textColor; this.bikesAllowed = r.bikesAllowed; this.sortOrder = r.sortOrder; - this.brandingUrl = r.brandingUrl; this.eligibilityRestricted = r.eligibilityRestricted; this.regionalFareCardAccepted = r.regionalFareCardAccepted; } @@ -210,14 +206,6 @@ public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; } - public String getBrandingUrl() { - return brandingUrl; - } - - public void setBrandingUrl(String brandingUrl) { - this.brandingUrl = brandingUrl; - } - public boolean hasEligibilityRestricted() { return eligibilityRestricted != MISSING_VALUE; } From 156ce41c774b0fadd2996c1a8f34134dae32c441 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:32:23 +0100 Subject: [PATCH 164/234] Use MAVEN_ARGS to suppress download --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1d2e8b66..b70035df3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,7 @@ jobs: env: LANG: ${{ matrix.locale }} + MAVEN_ARGS: "--no-transfer-progress" steps: - uses: actions/checkout@v4 @@ -42,7 +43,7 @@ jobs: cache: 'maven' - name: Test project with Maven - run: mvn --no-transfer-progress test package + run: mvn test package cli-integration-tests: From a02ccd205fc278047a0700d8328e25ba89ac67d4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:37:47 +0100 Subject: [PATCH 165/234] Remove route_bikes_allowed --- .../main/java/org/onebusaway/gtfs/model/Route.java | 14 -------------- .../gtfs/serialization/GtfsReaderTest.java | 1 - 2 files changed, 15 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java index d5c30bfcb..f7695e619 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Route.java @@ -58,10 +58,6 @@ public final class Route extends IdentityBean { @CsvField(name = "eligibility_restricted", optional = true, defaultValue = "-999") private int eligibilityRestricted = MISSING_VALUE; - - @Deprecated - @CsvField(name="route_bikes_allowed", optional = true, defaultValue = "0") - private int routeBikesAllowed = 0; /** * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed @@ -168,16 +164,6 @@ public String getTextColor() { public void setTextColor(String textColor) { this.textColor = textColor; } - - @Deprecated - public int getRouteBikesAllowed() { - return routeBikesAllowed; - } - - @Deprecated - public void setRouteBikesAllowed(int routeBikesAllowed) { - this.routeBikesAllowed = routeBikesAllowed; - } /** * @return 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java index 0ba05c737..cd6d2c596 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java @@ -193,7 +193,6 @@ public void testAllFields() throws IOException { assertEquals("route desc", route.getDesc()); assertEquals("FF0000", route.getColor()); assertEquals("0000FF", route.getTextColor()); - assertEquals(1, route.getRouteBikesAllowed()); assertEquals(2, route.getBikesAllowed()); assertEquals("http://agency.gov/route", route.getUrl()); assertEquals(100, route.getSortOrder()); From 991e50b45eaa40fb15f8803550fc51347b5190c9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Sep 2024 11:51:29 +0200 Subject: [PATCH 166/234] Remove support for many non-standard fields in trips.txt --- .../gtfs/model/GtfsMapping.hibernate.xml | 1 - .../onebusaway/gtfs/impl/GtfsMappingTest.java | 2 +- ...nateGtfsRelationalDaoImplCaltrainTest.java | 1 - ...dateTripHeadsignByDestinationStrategy.java | 15 --- .../impl/UpdateTripHeadsignByReference.java | 16 +--- ...UpdateTripHeadsignExcludeNonreference.java | 14 --- .../impl/UpdateTripHeadsignIfNull.java | 12 --- .../UpdateTripHeadsignRailRoadConvention.java | 2 +- .../updates/DeduplicateTripsStrategy.java | 2 - .../updates/LocalVsExpressUpdateStrategy.java | 1 - .../java/org/onebusaway/gtfs/model/Trip.java | 96 ------------------- .../gtfs/serialization/GtfsReaderTest.java | 1 - 12 files changed, 3 insertions(+), 160 deletions(-) diff --git a/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml b/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml index 5ad73ca66..d526058c5 100644 --- a/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml +++ b/onebusaway-gtfs-hibernate/src/main/resources/org/onebusaway/gtfs/model/GtfsMapping.hibernate.xml @@ -312,7 +312,6 @@ - diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java index 4ae4443bf..4f87d7d70 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/GtfsMappingTest.java @@ -44,7 +44,7 @@ public class GtfsMappingTest { private static GtfsReader _reader; @BeforeEach - public void setup() throws IOException { + public void setup() { Configuration config = new Configuration(); config = config.configure("org/onebusaway/gtfs/hibernate-configuration.xml"); diff --git a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java index 7ef7313fb..1e6cfa937 100644 --- a/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java +++ b/onebusaway-gtfs-hibernate/src/test/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImplCaltrainTest.java @@ -226,7 +226,6 @@ public void testGetTripById() { assertNull(trip.getBlockId()); assertEquals("0", trip.getDirectionId()); assertEquals(route, trip.getRoute()); - assertNull(trip.getRouteShortName()); assertEquals(aid("WD01272009"), trip.getServiceId()); assertEquals(aid("cal_sj_sf"), trip.getShapeId()); assertEquals("101", trip.getTripShortName()); diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByDestinationStrategy.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByDestinationStrategy.java index dae2df5b5..78ad76aa6 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByDestinationStrategy.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByDestinationStrategy.java @@ -50,23 +50,8 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { trip.setTripHeadsign(tripHeadSign); update++; } - else { - fallbackSetHeadsign(trip); - fallback++; - } - } - else { - fallbackSetHeadsign(trip); - fallback++; } } _log.info("trip headsign update:{} fallback: {}", update, fallback); } - - private void fallbackSetHeadsign (Trip trip) { - if (trip.getTripHeadsign() == null) { - trip.setTripHeadsign(trip.getRouteShortName()); - - } - } } diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByReference.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByReference.java index d963989ca..9a14aaaa6 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByReference.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignByReference.java @@ -44,7 +44,6 @@ public String getName() { public void run(TransformContext context, GtfsMutableRelationalDao dao) { GtfsMutableRelationalDao reference = (GtfsMutableRelationalDao) context.getReferenceReader().getEntityStore(); - String agency = reference.getAllTrips().iterator().next().getId().getAgencyId(); ArrayList missingStops = new ArrayList<>(); for (Trip trip : dao.getAllTrips()) { @@ -74,14 +73,11 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { } else { _log.error("No stoptimes for trip {} mta id", trip.toString(), trip.getMtaTripId()); - if (trip.getTripHeadsign() == null && trip.getRouteShortName() == null) { + if (trip.getTripHeadsign() == null) { //if trip has no headsign, no stoptimes and no shortname, remove it _log.error("Removing trip {}", trip.getId()); dao.removeEntity(trip); } - else { - genericSetHeadsign(trip); - } } } } @@ -91,16 +87,6 @@ private void fallbackSetHeadsign (Trip trip, Stop stop) { trip.setTripHeadsign(stop.getName()); //_log.info("Setting headsign {} on {}", stop.getName(), trip.toString()); } - else { - genericSetHeadsign(trip); - } - } - - private void genericSetHeadsign (Trip trip) { - if (trip.getRouteShortName() != null) { - trip.setTripHeadsign(trip.getRouteShortName()); - //_log.info("Setting headsign {} on {}", trip.getRouteShortName(), trip.toString()); - } } @CsvField(ignore = true) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignExcludeNonreference.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignExcludeNonreference.java index 190f40961..2746a4300 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignExcludeNonreference.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignExcludeNonreference.java @@ -62,10 +62,6 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { trip.setTripHeadsign(tripHeadSign); update++; } - else { - fallbackSetHeadsign(trip); - fallback++; - } } else { noChange++; @@ -74,20 +70,10 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { } } } - else { - fallbackSetHeadsign(trip); - fallback++; - } } _log.info("trip headsign update:{} fallback: {} no change: {} shuttle: {}", update, fallback, noChange, shuttle); } - private void fallbackSetHeadsign (Trip trip) { - if (trip.getTripHeadsign() == null) { - trip.setTripHeadsign(trip.getRouteShortName()); - } - } - @CsvField(ignore = true) private String _referenceAgencyId = null; private String getReferenceAgencyId(GtfsMutableRelationalDao dao) { diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignIfNull.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignIfNull.java index b8248a1d6..d242ebc37 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignIfNull.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignIfNull.java @@ -44,21 +44,9 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { if (tripHeadSign != null) { trip.setTripHeadsign(tripHeadSign); } - else { - fallbackSetHeadsign(trip); - } - } - else { - fallbackSetHeadsign(trip); } } } } - private void fallbackSetHeadsign (Trip trip) { - if (trip.getTripHeadsign() == null) { - trip.setTripHeadsign(trip.getRouteShortName()); - _log.info("Setting headsign to route short name: ", trip.getRouteShortName()); - } - } } diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignRailRoadConvention.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignRailRoadConvention.java index e6cc10b58..663ac0eef 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignRailRoadConvention.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/UpdateTripHeadsignRailRoadConvention.java @@ -48,7 +48,7 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { List stopTimes = dao.getStopTimesForTrip(trip); if (stopTimes != null && stopTimes.size() > 0) { - String existingTripHeadsign = (trip.getTripHeadsign() != null) ? trip.getTripHeadsign() : trip.getRouteShortName(); + String existingTripHeadsign = (trip.getTripHeadsign() != null) ? trip.getTripHeadsign() : "trip route short name"; Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(stopTimes.get(0).getDepartureTime() * 1000); diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/DeduplicateTripsStrategy.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/DeduplicateTripsStrategy.java index 1dc83b6fe..6df0c436d 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/DeduplicateTripsStrategy.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/DeduplicateTripsStrategy.java @@ -115,8 +115,6 @@ private String areTripsEquivalent(Trip tripA, Trip tripB) { return "directionId"; if (!equals(tripA.getRoute(), tripB.getRoute())) return "route"; - if (!equals(tripA.getRouteShortName(), tripB.getRouteShortName())) - return "routeShortName"; if (!equals(tripA.getShapeId(), tripB.getShapeId())) return "shapeId"; if (!equals(tripA.getTripHeadsign(), tripB.getTripHeadsign())) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/LocalVsExpressUpdateStrategy.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/LocalVsExpressUpdateStrategy.java index 087fdcb8f..d23c127a4 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/LocalVsExpressUpdateStrategy.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/updates/LocalVsExpressUpdateStrategy.java @@ -71,7 +71,6 @@ public void run(TransformContext context, GtfsMutableRelationalDao dao) { boolean isExpress = trip.getTripShortName().equals("EXPRESS"); if (isExpress) { _log.info("route(" + route.getShortName() + ") gets an E for trip " + trip.getId()); - trip.setRouteShortName(trip.getRoute().getShortName() + "E"); if (addLocalVsExpressToTripName) { String tripHeadsign = trip.getTripHeadsign(); if (tripHeadsign != null) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index 8881dd26c..439a8e69f 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -42,9 +42,6 @@ public final class Trip extends IdentityBean { @CsvField(optional = true) private String tripHeadsign; - @CsvField(optional = true) - private String routeShortName; - @CsvField(optional = true) private String directionId; @@ -57,27 +54,6 @@ public final class Trip extends IdentityBean { @CsvField(optional = true, defaultValue = "0") private int wheelchairAccessible = 0; - @CsvField(optional = true) - private String drtMaxTravelTime; - - @CsvField(optional = true) - private String drtAvgTravelTime; - - @CsvField(optional = true, defaultValue = "-1") - private double drtAdvanceBookMin; - - @CsvField(optional = true) - private String drtPickupMessage; - - @CsvField(optional = true) - private String drtDropOffMessage; - - @CsvField(optional = true) - private String continuousPickupMessage; - - @CsvField(optional = true) - private String continuousDropOffMessage; - @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double meanDurationFactor; @@ -147,18 +123,10 @@ public Trip(Trip obj) { this.serviceId = obj.serviceId; this.tripShortName = obj.tripShortName; this.tripHeadsign = obj.tripHeadsign; - this.routeShortName = obj.routeShortName; this.directionId = obj.directionId; this.blockId = obj.blockId; this.shapeId = obj.shapeId; this.wheelchairAccessible = obj.wheelchairAccessible; - this.drtMaxTravelTime = obj.drtMaxTravelTime; - this.drtAvgTravelTime = obj.drtAvgTravelTime; - this.drtAdvanceBookMin = obj.drtAdvanceBookMin; - this.drtPickupMessage = obj.drtPickupMessage; - this.drtDropOffMessage = obj.drtDropOffMessage; - this.continuousPickupMessage = obj.continuousPickupMessage; - this.continuousDropOffMessage = obj.continuousDropOffMessage; this.meanDurationFactor = obj.meanDurationFactor; this.meanDurationOffset = obj.meanDurationOffset; this.safeDurationFactor = obj.safeDurationFactor; @@ -213,14 +181,6 @@ public void setTripHeadsign(String tripHeadsign) { this.tripHeadsign = tripHeadsign; } - public String getRouteShortName() { - return routeShortName; - } - - public void setRouteShortName(String routeShortName) { - this.routeShortName = routeShortName; - } - public String getDirectionId() { return directionId; } @@ -253,62 +213,6 @@ public int getWheelchairAccessible() { return wheelchairAccessible; } - public String getDrtMaxTravelTime() { - return drtMaxTravelTime; - } - - public void setDrtMaxTravelTime(String drtMaxTravelTime) { - this.drtMaxTravelTime = drtMaxTravelTime; - } - - public String getDrtAvgTravelTime() { - return drtAvgTravelTime; - } - - public void setDrtAvgTravelTime(String drtAvgTravelTime) { - this.drtAvgTravelTime = drtAvgTravelTime; - } - - public double getDrtAdvanceBookMin() { - return drtAdvanceBookMin; - } - - public void setDrtAdvanceBookMin(double drtAdvanceBookMin) { - this.drtAdvanceBookMin = drtAdvanceBookMin; - } - - public String getDrtPickupMessage() { - return drtPickupMessage; - } - - public void setDrtPickupMessage(String drtPickupMessage) { - this.drtPickupMessage = drtPickupMessage; - } - - public String getDrtDropOffMessage() { - return drtDropOffMessage; - } - - public void setDrtDropOffMessage(String drtDropOffMessage) { - this.drtDropOffMessage = drtDropOffMessage; - } - - public String getContinuousPickupMessage() { - return continuousPickupMessage; - } - - public void setContinuousPickupMessage(String continuousPickupMessage) { - this.continuousPickupMessage = continuousPickupMessage; - } - - public String getContinuousDropOffMessage() { - return continuousDropOffMessage; - } - - public void setContinuousDropOffMessage(String continuousDropOffMessage) { - this.continuousDropOffMessage = continuousDropOffMessage; - } - public Double getMeanDurationFactor() { return meanDurationFactor; } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java index 0ba05c737..b43b6c78e 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java @@ -207,7 +207,6 @@ public void testAllFields() throws IOException { assertEquals("1", trip.getDirectionId()); assertEquals("B1", trip.getBlockId()); assertEquals(new AgencyAndId("1", "SHP1"), trip.getShapeId()); - assertEquals("10X", trip.getRouteShortName()); assertEquals(1, trip.getTripBikesAllowed()); assertEquals(2, trip.getBikesAllowed()); assertEquals(1, trip.getCarsAllowed()); From 2750a044d36d424b8ecd2f6888e1f15b0669b6f9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:43:41 +0100 Subject: [PATCH 167/234] Remove trip_bikes_allowed --- .../main/java/org/onebusaway/gtfs/model/Trip.java | 15 --------------- .../gtfs/serialization/GtfsReaderTest.java | 1 - 2 files changed, 16 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index 439a8e69f..f25976328 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -71,10 +71,6 @@ public final class Trip extends IdentityBean { private Double safeDurationOffset; - @Deprecated - @CsvField(optional = true, defaultValue = "0") - private int tripBikesAllowed = 0; - /** * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed */ @@ -131,7 +127,6 @@ public Trip(Trip obj) { this.meanDurationOffset = obj.meanDurationOffset; this.safeDurationFactor = obj.safeDurationFactor; this.safeDurationOffset = obj.safeDurationOffset; - this.tripBikesAllowed = obj.tripBikesAllowed; this.bikesAllowed = obj.bikesAllowed; this.carsAllowed = obj.carsAllowed; this.fareId = obj.fareId; @@ -245,16 +240,6 @@ public void setSafeDurationOffset(Double safeDurationOffset) { this.safeDurationOffset = safeDurationOffset; } - @Deprecated - public void setTripBikesAllowed(int tripBikesAllowed) { - this.tripBikesAllowed = tripBikesAllowed; - } - - @Deprecated - public int getTripBikesAllowed() { - return tripBikesAllowed; - } - /** * @return 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed */ diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java index b43b6c78e..10de00b46 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/GtfsReaderTest.java @@ -207,7 +207,6 @@ public void testAllFields() throws IOException { assertEquals("1", trip.getDirectionId()); assertEquals("B1", trip.getBlockId()); assertEquals(new AgencyAndId("1", "SHP1"), trip.getShapeId()); - assertEquals(1, trip.getTripBikesAllowed()); assertEquals(2, trip.getBikesAllowed()); assertEquals(1, trip.getCarsAllowed()); assertEquals(1, trip.getWheelchairAccessible()); From d4e521ec0b7afa4cd5cdcd50d8433ff4d9bc2cb8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:51:07 +0100 Subject: [PATCH 168/234] Remove fare_id --- .../main/java/org/onebusaway/gtfs/model/Trip.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index f25976328..1675149bf 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -84,10 +84,6 @@ public final class Trip extends IdentityBean { @CsvField(optional = true, defaultValue = "0") private int carsAllowed = 0; - // Custom extension for KCM to specify a fare per-trip - @CsvField(optional = true) - private String fareId; - // Custom extension for MNR @CsvField(optional = true, name = "note_id", mapping = EntityFieldMappingFactory.class, order = -1) private Note note; @@ -129,7 +125,6 @@ public Trip(Trip obj) { this.safeDurationOffset = obj.safeDurationOffset; this.bikesAllowed = obj.bikesAllowed; this.carsAllowed = obj.carsAllowed; - this.fareId = obj.fareId; this.note = obj.note; this.peakOffpeak = obj.peakOffpeak; this.mtaTripId = obj.mtaTripId; @@ -273,14 +268,6 @@ public void setCarsAllowed(int carsAllowed) { public String toString() { return ""; } - - public String getFareId() { - return fareId; - } - - public void setFareId(String fareId) { - this.fareId = fareId; - } public Note getNote() { return note; From c077775d7f6557932bb81fd306c54b985f446c7c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:53:22 +0100 Subject: [PATCH 169/234] Re-order properties --- .../main/java/org/onebusaway/gtfs/model/Trip.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java index 1675149bf..6030e1548 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Trip.java @@ -54,6 +54,12 @@ public final class Trip extends IdentityBean { @CsvField(optional = true, defaultValue = "0") private int wheelchairAccessible = 0; + /** + * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed + */ + @CsvField(optional = true, defaultValue = "0") + private int bikesAllowed = 0; + @Experimental(proposedBy = "https://github.com/MobilityData/gtfs-flex/pull/79") @CsvField(optional = true) private Double meanDurationFactor; @@ -70,13 +76,6 @@ public final class Trip extends IdentityBean { @CsvField(optional = true) private Double safeDurationOffset; - - /** - * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed - */ - @CsvField(optional = true, defaultValue = "0") - private int bikesAllowed = 0; - /** * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed */ From 7d70557d718274260a80ac145400dd5f55d014e1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 21:58:39 +0100 Subject: [PATCH 170/234] Add batch mode --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b70035df3..f8016e409 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: env: LANG: ${{ matrix.locale }} - MAVEN_ARGS: "--no-transfer-progress" + MAVEN_ARGS: "--batch-mode --no-transfer-progress" steps: - uses: actions/checkout@v4 From 4884e84876c7ea97cefd505535582a24aa774acb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 22:05:38 +0100 Subject: [PATCH 171/234] Add version --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8016e409..b8c4d624f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,9 @@ jobs: cache: 'maven' - name: Test project with Maven - run: mvn test package + run: | + mvn --version + mvn test package cli-integration-tests: From ce915085fd9646081e4b9973e01c921d8c2f34b9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 22:29:44 +0100 Subject: [PATCH 172/234] Use MAVEN_ARGS --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8c4d624f..aa0b91b27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,7 @@ jobs: - name: Test project with Maven run: | - mvn --version - mvn test package + mvn $MAVEN_ARGS test package cli-integration-tests: From e3ae9384c76603435fe53ec455aca3bf20cb44ed Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 22:37:33 +0100 Subject: [PATCH 173/234] Fix test reporter --- pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index 1ea43940f..aa6f41666 100644 --- a/pom.xml +++ b/pom.xml @@ -196,6 +196,15 @@ 1.3.0 + + plain + + true + + + + From 7e5912d0ac7916a4f7316fe45ffc73255c46fbf2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 2 Nov 2024 22:40:49 +0100 Subject: [PATCH 174/234] Finetune test reporting --- .github/workflows/ci.yml | 2 +- pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa0b91b27..2801b8270 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: env: LANG: ${{ matrix.locale }} - MAVEN_ARGS: "--batch-mode --no-transfer-progress" + MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" steps: - uses: actions/checkout@v4 diff --git a/pom.xml b/pom.xml index aa6f41666..93f14d47b 100644 --- a/pom.xml +++ b/pom.xml @@ -203,6 +203,7 @@ + UNICODE From d69d7acbd94a867a499196cdfdd8e68cc220d24b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 04:13:27 +0000 Subject: [PATCH 175/234] Update dependency org.hsqldb:hsqldb to v2.7.4 --- onebusaway-gtfs-hibernate/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 10fee17be..556f6aa75 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -29,7 +29,7 @@ org.hsqldb hsqldb - 2.7.3 + 2.7.4 test From 9e1b05aff8a3e8135cd95765a91800a0c1585046 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 10 Nov 2024 09:26:52 +0100 Subject: [PATCH 176/234] [maven-release-plugin] prepare release v4.0.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 7 +++---- 10 files changed, 12 insertions(+), 13 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 726f277fa..75d810a89 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index e9242e61c..4502f67ae 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 6160e8578..2112d5b70 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 556f6aa75..40b357d16 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index b69646721..28694a222 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.5-SNAPSHOT + 4.0.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index b8f9d42dd..068d1944a 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 3.2.5-SNAPSHOT + 4.0.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 57d18a8bc..3065938cf 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index ad458fb37..0ae15357d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 3be86a28d..0b7023248 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 93f14d47b..12c169732 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 3.2.5-SNAPSHOT + 4.0.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.0.0 @@ -201,8 +201,7 @@ true - + UNICODE From 58b6507874d9a6b75e101539507f3daed922fa59 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 10 Nov 2024 09:26:55 +0100 Subject: [PATCH 177/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 75d810a89..853956721 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 4502f67ae..cdaee2b09 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 2112d5b70..c7008284d 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 40b357d16..3b8e17537 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 28694a222..932e4f04d 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.0.0 + 4.0.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 068d1944a..d82a83bff 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.0.0 + 4.0.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 3065938cf..046ef4cd7 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 0ae15357d..8d618f017 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 0b7023248..3363a4b9c 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 12c169732..d030e8479 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.0 + 4.0.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.0.0 + HEAD From 2ad92b519615197ae50ce4e65039463a41f333dc Mon Sep 17 00:00:00 2001 From: Holger Bruch Date: Sun, 10 Nov 2024 12:01:30 +0100 Subject: [PATCH 178/234] Build docker images via jib-maven-plugin --- onebusaway-collections/pom.xml | 27 +++++++++++++++++++++++++++ onebusaway-csv-entities/pom.xml | 28 ++++++++++++++++++++++++++++ onebusaway-gtfs-hibernate/pom.xml | 29 ++++++++++++++++++++++++++++- onebusaway-gtfs-merge/pom.xml | 28 ++++++++++++++++++++++++++++ onebusaway-gtfs-transformer/pom.xml | 27 +++++++++++++++++++++++++++ onebusaway-gtfs/pom.xml | 28 ++++++++++++++++++++++++++++ pom.xml | 27 +++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 853956721..2d6e9fecc 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -28,4 +28,31 @@ + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index cdaee2b09..7e5fc032e 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -40,4 +40,32 @@ + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + + diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 3b8e17537..d1980ce01 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -52,6 +52,33 @@ jaxb-impl runtime - + + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index d82a83bff..e7c867364 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -37,4 +37,32 @@ test + + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 8d618f017..7bb306a4e 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -79,4 +79,31 @@ + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 3363a4b9c..0544b5b33 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -49,4 +49,32 @@ + + + + com.google.cloud.tools + jib-maven-plugin + + + true + + + + + io.github.zlika + reproducible-build-maven-plugin + 0.11 + + + run-when-packaged + + strip-jar + + package + + + + + + diff --git a/pom.xml b/pom.xml index d030e8479..7ae81ff60 100644 --- a/pom.xml +++ b/pom.xml @@ -206,6 +206,33 @@ + + com.google.cloud.tools + jib-maven-plugin + 3.4.4 + + + eclipse-temurin:21-jre + + + amd64 + linux + + + arm64 + linux + + + + + ${env.CONTAINER_REPO}/${project.artifactId}:${project.version} + + ${env.CONTAINER_REGISTRY_USER} + ${env.CONTAINER_REGISTRY_PASSWORD} + + + + From 170b3a50eb035abc2d2fba7702f68fa36d051012 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:39:50 +0000 Subject: [PATCH 179/234] Update dependency io.github.zlika:reproducible-build-maven-plugin to v0.17 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 2d6e9fecc..dd3976695 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -42,7 +42,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 7e5fc032e..0946a98af 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -54,7 +54,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index d1980ce01..be1cfa4b7 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -68,7 +68,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index e7c867364..20dc0c155 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -52,7 +52,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 7bb306a4e..a4aa91f7d 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -93,7 +93,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 0544b5b33..ac341db2e 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -63,7 +63,7 @@ io.github.zlika reproducible-build-maven-plugin - 0.11 + 0.17 run-when-packaged From ead1ef250392e9196ca882ae4f10a8faf0d91683 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 11 Nov 2024 09:44:36 +0100 Subject: [PATCH 180/234] Build container tarball on CI --- .github/workflows/ci.yml | 4 ++++ pom.xml | 43 ++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2801b8270..ec32fdab9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,10 @@ jobs: run: | mvn $MAVEN_ARGS test package + - name: Build container image tarball + run: | + mvn $MAVEN_ARGS jib:buildTar + cli-integration-tests: runs-on: ubuntu-latest diff --git a/pom.xml b/pom.xml index 7ae81ff60..a48dd14a5 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.onebusaway @@ -211,26 +212,26 @@ jib-maven-plugin 3.4.4 - - eclipse-temurin:21-jre - - - amd64 - linux - - - arm64 - linux - - - - - ${env.CONTAINER_REPO}/${project.artifactId}:${project.version} - - ${env.CONTAINER_REGISTRY_USER} - ${env.CONTAINER_REGISTRY_PASSWORD} - - + + eclipse-temurin:21-jre + + + amd64 + linux + + + arm64 + linux + + + + + ${env.CONTAINER_REPO}/${project.artifactId}:${project.version} + + ${env.CONTAINER_REGISTRY_USER} + ${env.CONTAINER_REGISTRY_PASSWORD} + + From 24ec8c9184817f1de83cd68c5766d90f118b6f38 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 11 Nov 2024 09:45:05 +0100 Subject: [PATCH 181/234] Remove hardcoded base image, jib should figure it out automatically --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index a48dd14a5..b92f84814 100644 --- a/pom.xml +++ b/pom.xml @@ -213,7 +213,6 @@ 3.4.4 - eclipse-temurin:21-jre amd64 From c6a70cca3818c311ad11c7cc6210f850061d2c56 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 11 Nov 2024 09:49:55 +0100 Subject: [PATCH 182/234] Skip testing when building image --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec32fdab9..e6706d1a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,8 @@ jobs: - name: Build container image tarball run: | - mvn $MAVEN_ARGS jib:buildTar + MAVEN_SKIP_ARGS="-Dmaven.test.skip=true -Dmaven.source.skip=true" + mvn $MAVEN_ARGS $MAVEN_SKIP_ARGS package jib:buildTar cli-integration-tests: From 3f0f297dfa9d3e58115b587e15035fc4f8cf837d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 11 Nov 2024 09:58:16 +0100 Subject: [PATCH 183/234] Get jib variables from Github --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6706d1a4..5c1a74c8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,10 @@ on: pull_request: branches: [ master ] + jobs: - unit-tests: + build: runs-on: ubuntu-latest strategy: @@ -17,6 +18,9 @@ jobs: env: LANG: ${{ matrix.locale }} MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" + CONTAINER_REPO: ${{ env.REGISTRY }} + CONTAINER_REGISTRY_USER: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 From d1a0b6f7e8b806d6852080e0c581d5a9e06a85e9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 12 Nov 2024 08:35:18 +0100 Subject: [PATCH 184/234] Update Dockerhub settings --- .github/workflows/ci.yml | 6 +++--- pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c1a74c8d..d708eef10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,9 +18,9 @@ jobs: env: LANG: ${{ matrix.locale }} MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" - CONTAINER_REPO: ${{ env.REGISTRY }} - CONTAINER_REGISTRY_USER: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_NAMESPACE: docker.io/opentransitsoftwarefoundation/ + CONTAINER_REGISTRY_USER: onebusawaybot + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} steps: - uses: actions/checkout@v4 diff --git a/pom.xml b/pom.xml index b92f84814..72b660604 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ - ${env.CONTAINER_REPO}/${project.artifactId}:${project.version} + ${env.CONTAINER_REGISTRY_NAMESPACE}/${project.artifactId}:${project.version} ${env.CONTAINER_REGISTRY_USER} ${env.CONTAINER_REGISTRY_PASSWORD} From 4bfd1e1fff890b98bb10a89da9fb1fc33c91ea24 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 12 Nov 2024 08:41:31 +0100 Subject: [PATCH 185/234] Run in separate step --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d708eef10..29c225646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,8 @@ on: pull_request: branches: [ master ] +env: + MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" jobs: @@ -17,10 +19,6 @@ jobs: env: LANG: ${{ matrix.locale }} - MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" - CONTAINER_REGISTRY_NAMESPACE: docker.io/opentransitsoftwarefoundation/ - CONTAINER_REGISTRY_USER: onebusawaybot - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} steps: - uses: actions/checkout@v4 @@ -50,13 +48,7 @@ jobs: run: | mvn $MAVEN_ARGS test package - - name: Build container image tarball - run: | - MAVEN_SKIP_ARGS="-Dmaven.test.skip=true -Dmaven.source.skip=true" - mvn $MAVEN_ARGS $MAVEN_SKIP_ARGS package jib:buildTar - cli-integration-tests: - runs-on: ubuntu-latest steps: @@ -70,3 +62,25 @@ jobs: - name: Run CLI integration tests run: ./cli-tests/cli-tests.sh + + container-image: + runs-on: ubuntu-latest + + env: + CONTAINER_REGISTRY_NAMESPACE: docker.io/opentransitsoftwarefoundation + CONTAINER_REGISTRY_USER: onebusawaybot + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'maven' + + - name: Build container image tarball + run: | + MAVEN_SKIP_ARGS="-Dmaven.test.skip=true -Dmaven.source.skip=true" + mvn $MAVEN_ARGS $MAVEN_SKIP_ARGS package jib:build From c4eff39fbc37e7f2cca0fbb58ac9703e17715fe6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 12 Nov 2024 08:53:21 +0100 Subject: [PATCH 186/234] Update rule for running CI job --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29c225646..2c9d90c5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: run: ./cli-tests/cli-tests.sh container-image: + if: github.repository_owner == 'onebusaway' && github.event_name == 'push' && github.ref == 'refs/heads/master' runs-on: ubuntu-latest env: From 3fc1071870afc27ee04a15279de78a3736e01f23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Nov 2024 03:15:25 +0000 Subject: [PATCH 187/234] Update dependency org.junit.jupiter:junit-jupiter-api to v5.11.3 (#304) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 72b660604..5b674549f 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ UTF-8 2.0.16 0.0.13 - 5.11.2 + 5.11.3 From 6c56e27fb72b657d615eb025fad0fe8ad08516b2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Nov 2024 03:20:14 +0000 Subject: [PATCH 188/234] Update Maven plugins (#305) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5b674549f..cbd66c371 100644 --- a/pom.xml +++ b/pom.xml @@ -149,7 +149,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.10.1 + 3.11.1 none false @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.1 + 3.5.2 me.fabriciorby From c9c6682e8c69c7e52d275b858aaf4ee4751b2497 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 01:37:36 +0000 Subject: [PATCH 189/234] Update dependency com.fasterxml.jackson.core:jackson-databind to v2.18.2 --- onebusaway-gtfs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index ac341db2e..159217b22 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -26,7 +26,7 @@ com.fasterxml.jackson.core jackson-databind - 2.18.1 + 2.18.2 de.grundid.opendatalab From 99682a54ab4c7209ec519b8122fae1a6c722ce85 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 03:34:01 +0000 Subject: [PATCH 190/234] Update dependency io.github.classgraph:classgraph to v4.8.179 (#307) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- onebusaway-collections/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index dd3976695..c6cc0ba4e 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -24,7 +24,7 @@ io.github.classgraph classgraph - 4.8.177 + 4.8.179 From dcba065c99d91ce6fc41db7581ffb91c4270814e Mon Sep 17 00:00:00 2001 From: Scott Berkley <981852+sberkley@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:38:57 -0800 Subject: [PATCH 191/234] clean up capitalization and whitespace --- .../gtfs/impl/HibernateGtfsRelationalDaoImpl.java | 13 +++++++------ .../java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 6d89eb108..d2fc91295 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -70,12 +70,12 @@ public T getEntityForId(Class type, Serializable id) { @Override public List getAllAgencies() { - return _ops.find("from Agency"); + return _ops.find("FROM Agency"); } @Override public List getAllBlocks() { - return _ops.find("from Block"); + return _ops.find("FROM Block"); } @Override @@ -294,7 +294,7 @@ public Trip getTripForId(AgencyAndId id) { @Override public Collection getAllAreas() { - return _ops.find("from Area"); + return _ops.find("FROM Area"); } @Deprecated @@ -327,7 +327,7 @@ public Collection getAllLocationGroups() { } @Override public Collection getAllStopAreas() { - return _ops.find("from StopArea"); + return _ops.find("FROM StopArea"); } @Override public Collection getAllLocations() { @@ -336,12 +336,13 @@ public Collection getAllLocations() { @Override public Collection getAllBookingRules() { - return _ops.find("from BookingRule"); + return _ops.find("FROM BookingRule"); } @Override public Collection getAllTranslations() { - return _ops.find("from Translation"); + return _ops.find("FROM Translation"); + } } @Override diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java index 772a21ddc..637b967d9 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java @@ -146,7 +146,9 @@ public Collection getAllRiderships() { return getAllEntitiesForType(Ridership.class); } - public Collection getAllVehicles() { return getAllEntitiesForType(Vehicle.class); } + public Collection getAllVehicles() { + return getAllEntitiesForType(Vehicle.class); + } public Collection getAllLevels() { return getAllEntitiesForType(Level.class); From 9b10e5b445e799960ef37da742f74b03c18751d2 Mon Sep 17 00:00:00 2001 From: Scott Berkley <981852+sberkley@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:39:48 -0800 Subject: [PATCH 192/234] Add Networks model Network model for networks.txt File has network_id and network_name --- .../impl/HibernateGtfsRelationalDaoImpl.java | 4 + .../org/onebusaway/gtfs/impl/GtfsDaoImpl.java | 3 + .../gtfs/impl/GtfsDataServiceImpl.java | 5 + .../org/onebusaway/gtfs/model/Network.java | 55 ++++++++ .../GtfsEntitySchemaFactory.java | 1 + .../gtfs/serialization/GtfsReader.java | 1 + .../org/onebusaway/gtfs/services/GtfsDao.java | 6 + .../onebusaway/gtfs/model/NetworkTest.java | 117 ++++++++++++++++++ 8 files changed, 192 insertions(+) create mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Network.java create mode 100644 onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/NetworkTest.java diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index d2fc91295..67bcdfbb2 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -343,6 +343,10 @@ public Collection getAllBookingRules() { public Collection getAllTranslations() { return _ops.find("FROM Translation"); } + + @Override + public Collection getAllNetworks() { + return _ops.find("FROM Network"); } @Override diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java index 637b967d9..f814ee124 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java @@ -259,6 +259,9 @@ public Vehicle getVehicleForId(AgencyAndId id) { return getEntityForId(Vehicle.class, id); } + public Collection getAllNetworks() { + return getAllEntitiesForType(Network.class); + } public Facility getFacilityForId(AgencyAndId id) { return getEntityForId(Facility.class, id);} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java index 2cd915d98..d67aa9df4 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java @@ -403,6 +403,11 @@ public Collection getAllTranslations() { return _dao.getAllTranslations(); } + @Override + public Collection getAllNetworks() { + return _dao.getAllNetworks(); + } + @Override public List getOptionalMetadataFilenames() { return _dao.getOptionalMetadataFilenames(); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Network.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Network.java new file mode 100644 index 000000000..f07be5940 --- /dev/null +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Network.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2024 Sound Transit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs.model; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.csv_entities.schema.annotations.CsvFields; +import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; + +@CsvFields(filename = "networks.txt", required = false) +public final class Network extends IdentityBean { + + private static final long serialVersionUID = 1L; + + @CsvField(name = "network_id", mapping = DefaultAgencyIdFieldMappingFactory.class) + private AgencyAndId id; + + @CsvField(name = "network_name", optional = true) + private String name; + + @Override + public AgencyAndId getId() { + return id; + } + + @Override + public void setId(AgencyAndId id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return ""; + } +} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java index f630683d1..c6df1c68f 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsEntitySchemaFactory.java @@ -76,6 +76,7 @@ public static List> getEntityClasses() { entityClasses.add(DirectionEntry.class); entityClasses.add(AlternateStopNameException.class); entityClasses.add(Icon.class); + entityClasses.add(Network.class); return entityClasses; } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index d44571bca..a3a09b8a7 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -104,6 +104,7 @@ public GtfsReader() { _entityClasses.add(WrongWayConcurrency.class); _entityClasses.add(DirectionEntry.class); _entityClasses.add(AlternateStopNameException.class); + _entityClasses.add(Network.class); CsvTokenizerStrategy tokenizerStrategy = new CsvTokenizerStrategy(); tokenizerStrategy.getCsvParser().setTrimInitialWhitespace(true); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java index 9311b7381..9d51be156 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java @@ -220,6 +220,12 @@ public interface GtfsDao extends GenericDao { public Collection getAllTranslations(); + /**** + * {@link Network} Methods + ****/ + + public Collection getAllNetworks(); + public Collection getAllDirectionEntries(); public Collection getAllWrongWayConcurrencies(); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/NetworkTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/NetworkTest.java new file mode 100644 index 000000000..552906552 --- /dev/null +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/model/NetworkTest.java @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2024 Sound Transit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs.model; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onebusaway.gtfs.serialization.GtfsWriter; +import org.onebusaway.gtfs.serialization.GtfsWriterTest; +import org.onebusaway.gtfs.services.MockGtfs; +import org.onebusaway.gtfs.services.GtfsRelationalDao; + +import static org.junit.jupiter.api.Assertions.*; + +public class NetworkTest { + + private MockGtfs _gtfs; + + private File _tmpDirectory; + + @BeforeEach + public void before() throws IOException { + _gtfs = MockGtfs.create(); + + //make temp directory for gtfs writing output + _tmpDirectory = File.createTempFile("NetworkTest-", "-tmp"); + if (_tmpDirectory.exists()) + GtfsWriterTest.deleteFileRecursively(_tmpDirectory); + _tmpDirectory.mkdirs(); + } + + @Test + public void testBasicNetworks() throws IOException { + _gtfs.putMinimal(); + _gtfs.putDefaultTrips(); + _gtfs.putDefaultStops(); + _gtfs.putLines("networks.txt", + "network_id,network_name", + "rail_network,Rail Network", "bus_network,Bus Network"); + + GtfsRelationalDao dao = _gtfs.read(); + assertEquals(2, dao.getAllNetworks().size()); + + GtfsWriter writer = new GtfsWriter(); + writer.setOutputLocation(_tmpDirectory); + writer.run(dao); + + Scanner scan = new Scanner(new File(_tmpDirectory + "/networks.txt")); + Set expectedNetworkNames = new HashSet(); + Set foundNetworkNames = new HashSet(); + expectedNetworkNames.add("Rail Network"); + expectedNetworkNames.add("Bus Network"); + boolean onHeader = true; + while(scan.hasNext()){ + String line = scan.nextLine(); + if (onHeader) { + onHeader = false; + } else { + String[] lineParts = line.split(","); + + if (lineParts.length > 1) { + foundNetworkNames.add(lineParts[1]); + } + } + } + scan.close(); + + assertEquals(expectedNetworkNames, foundNetworkNames, "Did not find network names in output"); + } + + @Test + public void testPutMinimal() throws IOException { + _gtfs.putMinimal(); + // Just make sure it parses without throwing an error. + _gtfs.read(); + } + + @AfterEach + public void teardown() { + deleteFileRecursively(_tmpDirectory); + } + + public static void deleteFileRecursively(File file) { + + if (!file.exists()) + return; + + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File child : files) + deleteFileRecursively(child); + } + } + + file.delete(); + } + +} + From 3f7ec6fbb8d5515265cff4d211fe19b82a0f22d2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Dec 2024 08:31:30 +0100 Subject: [PATCH 193/234] [maven-release-plugin] prepare release v4.1.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 7 +++---- 10 files changed, 12 insertions(+), 13 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index c6cc0ba4e..402d1b48a 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 0946a98af..e2e22c17f 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index c7008284d..0d2294da0 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index be1cfa4b7..52e6b314e 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 932e4f04d..1ad308cc1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.0.1-SNAPSHOT + 4.1.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 20dc0c155..0750bc790 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.0.1-SNAPSHOT + 4.1.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 046ef4cd7..ca9e77937 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a4aa91f7d..a89fa2191 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 159217b22..91bfe3b4e 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 ../pom.xml diff --git a/pom.xml b/pom.xml index cbd66c371..d44a19591 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.onebusaway onebusaway-gtfs-modules - 4.0.1-SNAPSHOT + 4.1.0 pom onebusaway-gtfs-modules @@ -39,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.1.0 From 8bd17cf201df8e831147e8f567f01de71bdd00b4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Dec 2024 08:31:31 +0100 Subject: [PATCH 194/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 402d1b48a..a9aa88607 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index e2e22c17f..74fcd0f86 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 0d2294da0..6a3076552 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 52e6b314e..3d30a363a 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 1ad308cc1..9c4a5a456 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.1.0 + 4.1.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 0750bc790..305f4752d 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.1.0 + 4.1.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index ca9e77937..47c58f032 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a89fa2191..6c8126758 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 91bfe3b4e..dd25ee069 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index d44a19591..de90c6314 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.0 + 4.1.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.1.0 + HEAD From 09e64450d8994b9cb851af0095db2602a9a3e17d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 3 Dec 2024 08:37:19 +0100 Subject: [PATCH 195/234] Include non-breaking changes in release notes --- .github/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/release.yml b/.github/release.yml index 85bf4d649..a03049b4e 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -6,6 +6,9 @@ changelog: - title: Bugfixes labels: - bug + - title: Non-breaking changes + labels: + - "*" - title: Dependency upgrades labels: - dependencies From d60a1141cda5fc6be008a00b8b0503e4463f94ba Mon Sep 17 00:00:00 2001 From: sbonnet Date: Fri, 3 May 2024 10:50:05 +0200 Subject: [PATCH 196/234] Add "remove_today" attribute (cherry picked from commit 05c71727aed0d3afd5fb672dc89f12c27b665e1e) --- .../impl/RemoveOldCalendarStatements.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java index 434672c77..864ad9c96 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java @@ -15,10 +15,13 @@ */ package org.onebusaway.gtfs_transformer.impl; +import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.gtfs.model.ServiceCalendar; +import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; import org.onebusaway.gtfs_transformer.services.TransformContext; +import org.onebusaway.gtfs_transformer.util.CalendarFunctions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +31,17 @@ public class RemoveOldCalendarStatements implements GtfsTransformStrategy { private final Logger _log = LoggerFactory.getLogger(RemoveOldCalendarStatements.class); + + @CsvField(optional = true) + private boolean removeToday = true; + + @CsvField(ignore = true) + private CalendarFunctions helper = new CalendarFunctions(); + + public void setRemoveToday(boolean removeToday) { + this.removeToday = removeToday; + } + @Override public String getName() { return this.getClass().getSimpleName(); @@ -37,8 +51,13 @@ public String getName() { public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { RemoveEntityLibrary removeEntityLibrary = new RemoveEntityLibrary(); Set serviceCalendarsToRemove = new HashSet(); + java.util.Date today = new java.util.Date(); + + if(!removeToday) { + today = helper.removeTime(today); + } + for (ServiceCalendar calendar: gtfsMutableRelationalDao.getAllCalendars()) { - java.util.Date today = new java.util.Date(); if (calendar.getEndDate().getAsDate().before(today)){ serviceCalendarsToRemove.add(calendar); } From bbb72a5d62d9e43d6df4a73c6a5958edef08e273 Mon Sep 17 00:00:00 2001 From: sbonnet Date: Fri, 3 May 2024 14:04:43 +0200 Subject: [PATCH 197/234] Clean, comment and format --- .../impl/RemoveOldCalendarStatements.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java index 864ad9c96..5a9e0b982 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java @@ -15,23 +15,22 @@ */ package org.onebusaway.gtfs_transformer.impl; +import java.util.HashSet; +import java.util.Set; + import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.gtfs.model.ServiceCalendar; -import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; import org.onebusaway.gtfs_transformer.services.TransformContext; import org.onebusaway.gtfs_transformer.util.CalendarFunctions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; +/** + * Remove calendar dates that are past. + * + * remove_today: delete today's calendar dates if true. Default value is true + */ public class RemoveOldCalendarStatements implements GtfsTransformStrategy { - - private final Logger _log = LoggerFactory.getLogger(RemoveOldCalendarStatements.class); - @CsvField(optional = true) private boolean removeToday = true; @@ -53,12 +52,12 @@ public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfs Set serviceCalendarsToRemove = new HashSet(); java.util.Date today = new java.util.Date(); - if(!removeToday) { + if (!removeToday) { today = helper.removeTime(today); } - for (ServiceCalendar calendar: gtfsMutableRelationalDao.getAllCalendars()) { - if (calendar.getEndDate().getAsDate().before(today)){ + for (ServiceCalendar calendar : gtfsMutableRelationalDao.getAllCalendars()) { + if (calendar.getEndDate().getAsDate().before(today)) { serviceCalendarsToRemove.add(calendar); } } From c17f67b0a9fe7ff01bd7ebedb8afb931f695cb39 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Tue, 3 Dec 2024 17:12:11 +0100 Subject: [PATCH 198/234] Add test case to validate the removal of calendars with today's date --- .../impl/RemoveOldCalendarStatementsTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java new file mode 100644 index 000000000..e246045d1 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java @@ -0,0 +1,71 @@ +package org.onebusaway.gtfs_transformer.impl; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.junit.Before; +import org.junit.Test; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs.services.MockGtfs; +import org.onebusaway.gtfs_transformer.services.TransformContext; + +public class RemoveOldCalendarStatementsTest { + + private RemoveOldCalendarStatements removeOldCalendarStatements = new RemoveOldCalendarStatements(); + private TransformContext _context = new TransformContext(); + private MockGtfs _gtfs; + + @Before + public void setup() throws IOException{ + + _gtfs = MockGtfs.create(); + _gtfs.putAgencies(1); + _gtfs.putStops(1); + _gtfs.putRoutes(1); + _gtfs.putTrips(1, "r0", "sid0"); + _gtfs.putStopTimes("t0", "s0"); + + // Insert a calendar entry wtih start and end dates set to today's date + String startDate = getCurrentDateFormatted(-3); + String endDate = getCurrentDateFormatted(null); + + _gtfs.putCalendars( + 1, + "start_date="+startDate, + "end_date="+endDate + ); + } + + @Test + public void testRemoveCalendarForToday() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + // Set removeToday to true to allow the removal of the calendar for today's date + removeOldCalendarStatements.setRemoveToday(true); + removeOldCalendarStatements.run(_context, dao); + // Verify that GtfsMutableRelationalDao object no longer contains any calendar entries after removing the calendar for today's date + assertEquals(0,dao.getAllCalendars().size()); + } + + @Test + public void testRemoveCalendar() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + removeOldCalendarStatements.setRemoveToday(false); + removeOldCalendarStatements.run(_context, dao); + // Verify that GtfsMutableRelationalDao object still contain the initially added calendar entry + assertEquals(1,dao.getAllCalendars().size()); + } + + // Helper function to get today's date in the required format + public static String getCurrentDateFormatted(Integer daysOffset) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + LocalDate date = LocalDate.now(); + if (daysOffset != null){ + date = date.plusDays(daysOffset); + } + // Format date as "yyyyMMdd" + return date.format(formatter); + } +} From 578bc28872c7625847913fb23bca6b76eeb3494f Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Fri, 3 May 2024 13:30:31 +0200 Subject: [PATCH 199/234] TruncatenewCalendarStatements: add new argument --- .../impl/TruncateNewCalendarStatements.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatements.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatements.java index ef2456493..c4c2bca26 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatements.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatements.java @@ -15,6 +15,7 @@ */ package org.onebusaway.gtfs_transformer.impl; +import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.gtfs.model.ServiceCalendar; import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; @@ -35,6 +36,31 @@ public class TruncateNewCalendarStatements implements GtfsTransformStrategy { private final Logger _log = LoggerFactory.getLogger(TruncateNewCalendarStatements.class); + + /* + * add two arguments used in the truncated transformation strategy + * calendarField --> calendar_field (json config file) + * Calendar.YEAR = 1 + * Calendar.MONTH = 2 + * Calendar.DAY_OF_MONTH = 5 + * Calendar.DAY_OF_YEAR = 6 + * calendarAmount --> calendar_amount (json config file) + */ + @CsvField(optional = true) + private int calendarField = Calendar.MONTH; + + @CsvField(optional = true) + private int calendarAmount = 1; + + + public void setCalendarField(int calendarField) { + this.calendarField = calendarField; + } + + public void setCalendarAmount(int calendarAmount) { + this.calendarAmount = calendarAmount; + } + @Override public String getName() { return this.getClass().getSimpleName(); @@ -43,9 +69,8 @@ public String getName() { @Override public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { RemoveEntityLibrary removeEntityLibrary = new RemoveEntityLibrary(); - // TODO make this an argument -- default to one month from now Calendar c = Calendar.getInstance(); - c.roll(Calendar.MONTH, 1); + c.add(calendarField, calendarAmount); java.util.Date oneMonthFromNow = c.getTime(); Set serviceCalendarsToRemove = new HashSet(); for (ServiceCalendar calendar: gtfsMutableRelationalDao.getAllCalendars()) { From 907653aa43c9820f92aa903b7156b720b90153bb Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Thu, 5 Dec 2024 09:50:54 +0100 Subject: [PATCH 200/234] Fix junit issue + add documentation --- docs/onebusaway-gtfs-transformer-cli.md | 23 ++++++++++++++++++- .../impl/RemoveOldCalendarStatementsTest.java | 11 ++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 8bd625bf2..069fe6a16 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -23,6 +23,7 @@ * [Trim a Trip](#trim-a-trip) * [Generate Stop Times](#generate-stop-times) * [Extend Service Calendars](#extend-service-calendars) + * [Remove Old Calendar Statements](#remove-old-calendar-statements) * [Deduplicate Calendar Entries](#deduplicate-calendar-entries) * [Merge Trips and Simplify Calendar Entries](#merge-trips-and-simplify-calendar-entries) * [Shift Negative Stop Times](#shift-negative-stop-times) @@ -337,7 +338,27 @@ Calendars that have expired before the specified date will be considered inactiv _Note_: We don't make any effort to extend canceled service dates, as specified in `calendar_dates.txt` for holidays and other special events. It's too tricky to automatically determine how they should be handled. You may need to still handle -those manually. +those manually. + +#### Remove Old Calendar Statements + +RemoveOldCalendarStatements is an operation designed to remove calendar entries that are no longer valid on today's date. + +By default, it deletes entries from the calendar.txt file whose end_date field has passed, including those valid until today. + +With the remove_today attribute added to the JSON transformer snippet, users can control whether calendar entries valid for today are included or excluded in the output GTFS. + + * If remove_today is set to true or not specified, the transformer will remove the calendar entries for the current date. + +``` + {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":true} +``` + + * If remove_today is set to false, the transformer will retain the calendar entries for the current date. + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":false} +``` #### Deduplicate Calendar Entries diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java index e246045d1..7706d48dc 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java @@ -1,13 +1,12 @@ package org.onebusaway.gtfs_transformer.impl; -import static org.junit.Assert.assertEquals; - +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; import org.onebusaway.gtfs_transformer.services.TransformContext; @@ -18,7 +17,7 @@ public class RemoveOldCalendarStatementsTest { private TransformContext _context = new TransformContext(); private MockGtfs _gtfs; - @Before + @BeforeEach public void setup() throws IOException{ _gtfs = MockGtfs.create(); @@ -68,4 +67,4 @@ public static String getCurrentDateFormatted(Integer daysOffset) { // Format date as "yyyyMMdd" return date.format(formatter); } -} +} \ No newline at end of file From 03b2b2508a6b192a5491205fe9e4fa1e5c201497 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Thu, 5 Dec 2024 14:10:38 +0100 Subject: [PATCH 201/234] Add test file and document truncate operation --- docs/onebusaway-gtfs-transformer-cli.md | 41 +++++++++ .../TruncateNewCalendarStatementsTest.java | 90 +++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatementsTest.java diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 8bd625bf2..b5ea3b73d 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -24,6 +24,7 @@ * [Generate Stop Times](#generate-stop-times) * [Extend Service Calendars](#extend-service-calendars) * [Deduplicate Calendar Entries](#deduplicate-calendar-entries) + * [Truncate New Calendar Statements](#truncate-new-calendar-statements) * [Merge Trips and Simplify Calendar Entries](#merge-trips-and-simplify-calendar-entries) * [Shift Negative Stop Times](#shift-negative-stop-times) * [Arbitrary Transform](#arbitrary-transform) @@ -347,6 +348,46 @@ ids to a single service_id entry. ``` {"op":"deduplicate_service_ids"} ``` + +#### Truncate New Calendar Statements + +This operation truncates calendar and calendar date entries based on the configuration attributes in the JSON transformer snippet: + + * calendar_field: Specifies the unit of time for truncation. It can have one of the following values: + - `Calendar.YEAR` = 1 + - `Calendar.MONTH` = 2 (default) + - `Calendar.DAY_OF_MONTH` = 5 + - `Calendar.DAY_OF_YEAR` = 6 + + * calendar_amount: Specifies the number of units to truncate entries. + The value is an integer representing the amount (default = 1). + +Both `calendar_field` and `calendar_amount` must be provided as integers in the JSON transformer. + +If these parameters are not specified, the default behavior is truncation by 1 month. + +Example : + +Truncate calendar and calendar dates to the next 21 days: + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} +``` + +Truncate entries to the next 3 months: + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":2,"calendar_amount":3} +``` + +Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. + +Without this retain operation, the `trips.txt` file will contain trips with non-existent calendar dates, leading to invalid data. + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} +{"op":"retain", "match":{"file":"calendar_dates.txt"}, "retainBlocks":false} +``` #### Merge Trips and Simplify Calendar Entries diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatementsTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatementsTest.java new file mode 100644 index 000000000..7576a48ab --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TruncateNewCalendarStatementsTest.java @@ -0,0 +1,90 @@ +package org.onebusaway.gtfs_transformer.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs.services.MockGtfs; +import org.onebusaway.gtfs_transformer.services.TransformContext; + +public class TruncateNewCalendarStatementsTest { + + private TruncateNewCalendarStatements truncateNewCalendarStatements = new TruncateNewCalendarStatements(); + private TransformContext _context = new TransformContext(); + private MockGtfs _gtfs; + + @BeforeEach + public void setup() throws IOException{ + + _gtfs = MockGtfs.create(); + _gtfs.putAgencies(1); + _gtfs.putStops(1); + _gtfs.putRoutes(1); + _gtfs.putTrips(1, "r0", "sid0"); + _gtfs.putStopTimes("t0", "s0"); + + // Set startDate to today's date and endDate to three weeks from today + String startDate = getCurrentDateFormatted(null); + String endDate = getCurrentDateFormatted(21); + + // Define additional dates for testing purposes, relative to startDate + String threeDaysFromStartDate = getCurrentDateFormatted(5); + String sevenDaysFromStartDate = getCurrentDateFormatted(7); + String tenDaysFromStartDate = getCurrentDateFormatted(10); + String fifteenDaysFromStartDate = getCurrentDateFormatted(15); + + // Insert a calendar entry with startDate set to today and endDate set to 3 weeks from today + _gtfs.putCalendars( + 1, + "start_date="+startDate, + "end_date="+endDate + ); + + // Insert calendar dates entries + _gtfs.putCalendarDates("sid0="+startDate+","+threeDaysFromStartDate+","+ + sevenDaysFromStartDate+","+tenDaysFromStartDate+","+fifteenDaysFromStartDate); + } + + @Test + public void testTruncateCalendarStatementsWithinThreeDays() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + // Set calendarField 6 -> Calendar.DAY_OF_YEAR + truncateNewCalendarStatements.setCalendarField(6); + // Set calendarAmount : 3 -> 3 Days + truncateNewCalendarStatements.setCalendarAmount(3); + truncateNewCalendarStatements.run(_context, dao); + + // Verify that GtfsMutableRelationalDao object contains exactly one calendar and one calendar date entry + assertEquals(1,dao.getAllCalendars().size()); + assertEquals(1,dao.getAllCalendarDates().size()); + } + + @Test + public void testTruncateCalendarStatementsWithinSevenDays() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + // Set calendarField 6 -> Calendar.DAY_OF_YEAR + truncateNewCalendarStatements.setCalendarField(6); + // Set calendarAmount : 7 -> 7 Days + truncateNewCalendarStatements.setCalendarAmount(7); + truncateNewCalendarStatements.run(_context, dao); + // Verify that GtfsMutableRelationalDao object contains exactly one calendar and three calendar dates entries + assertEquals(1,dao.getAllCalendars().size()); + assertEquals(3,dao.getAllCalendarDates().size()); + } + + // Helper function to get today's date in the required format + public static String getCurrentDateFormatted(Integer daysOffset) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + LocalDate date = LocalDate.now(); + if (daysOffset != null){ + date = date.plusDays(daysOffset); + } + // Format date as "yyyyMMdd" + return date.format(formatter); + } + +} \ No newline at end of file From bee8de30b89760c8b9dec00c796f31b0d7ef6c61 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 07:34:41 +0100 Subject: [PATCH 202/234] [maven-release-plugin] prepare release v4.2.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index a9aa88607..000cf8aec 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 74fcd0f86..9cea0542b 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 6a3076552..6c3a964f3 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 3d30a363a..7d0d83bc0 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 9c4a5a456..56e8085b5 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.1.1-SNAPSHOT + 4.2.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 305f4752d..54fdef6b5 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.1.1-SNAPSHOT + 4.2.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 47c58f032..a94cfe6e6 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 6c8126758..b42453991 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index dd25ee069..4b72c4607 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/pom.xml b/pom.xml index de90c6314..0b571ec4e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.1.1-SNAPSHOT + 4.2.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.2.0 From e451796c450ddba1c54c3566b3b3dcabcaa4c284 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 07:34:43 +0100 Subject: [PATCH 203/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 000cf8aec..2484a0e74 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 9cea0542b..2e01cea1a 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 6c3a964f3..04503b194 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 7d0d83bc0..a55eebba3 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 56e8085b5..3535350e0 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.2.0 + 4.2.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 54fdef6b5..bdbe0b587 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.2.0 + 4.2.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index a94cfe6e6..ba5cf3b9b 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index b42453991..3398e8945 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 4b72c4607..11d5f3672 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 0b571ec4e..6e8ae48d5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.0 + 4.2.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.2.0 + HEAD From 3b0bf35c11cbb0fcf104f42f0da3768571866905 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Fri, 6 Dec 2024 11:04:43 +0100 Subject: [PATCH 204/234] Change default behaviour: set the default value of removeToday to false in the remove old calendar statements operation --- docs/onebusaway-gtfs-transformer-cli.md | 6 +++--- .../gtfs_transformer/impl/RemoveOldCalendarStatements.java | 4 ++-- .../impl/RemoveOldCalendarStatementsTest.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 069fe6a16..f590f7b85 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -344,17 +344,17 @@ those manually. RemoveOldCalendarStatements is an operation designed to remove calendar entries that are no longer valid on today's date. -By default, it deletes entries from the calendar.txt file whose end_date field has passed, including those valid until today. +By default, it deletes entries from the calendar.txt file whose end_date field has passed. With the remove_today attribute added to the JSON transformer snippet, users can control whether calendar entries valid for today are included or excluded in the output GTFS. - * If remove_today is set to true or not specified, the transformer will remove the calendar entries for the current date. + * If remove_today is set to true, the transformer will remove the calendar entries for the current date. ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":true} ``` - * If remove_today is set to false, the transformer will retain the calendar entries for the current date. + * If remove_today is set to false or not specified, the transformer will retain the calendar entries for the current date. ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":false} diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java index 5a9e0b982..5fa491838 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java @@ -28,11 +28,11 @@ /** * Remove calendar dates that are past. * - * remove_today: delete today's calendar dates if true. Default value is true + * remove_today: delete today's calendar dates if true. Default value is false */ public class RemoveOldCalendarStatements implements GtfsTransformStrategy { @CsvField(optional = true) - private boolean removeToday = true; + private boolean removeToday = false; @CsvField(ignore = true) private CalendarFunctions helper = new CalendarFunctions(); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java index 7706d48dc..3519bfc26 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java @@ -51,7 +51,7 @@ public void testRemoveCalendarForToday() throws IOException { @Test public void testRemoveCalendar() throws IOException { GtfsMutableRelationalDao dao = _gtfs.read(); - removeOldCalendarStatements.setRemoveToday(false); + // Keep the default value as false and do not change it removeOldCalendarStatements.run(_context, dao); // Verify that GtfsMutableRelationalDao object still contain the initially added calendar entry assertEquals(1,dao.getAllCalendars().size()); From 885ed7d35f3c18c578a4ed7aba5adec1853cded4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 11:11:53 +0100 Subject: [PATCH 205/234] [maven-release-plugin] prepare release v4.3.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 2484a0e74..79540bceb 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 2e01cea1a..81161616a 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 04503b194..69646ea3e 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index a55eebba3..81e8b81a4 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 3535350e0..9b3e9e462 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.2.1-SNAPSHOT + 4.3.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index bdbe0b587..97f27ffb9 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.2.1-SNAPSHOT + 4.3.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index ba5cf3b9b..032e5d591 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 3398e8945..f82ee9aaa 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 11d5f3672..21abe34d4 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 6e8ae48d5..9217b8092 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.2.1-SNAPSHOT + 4.3.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.3.0 From c148b5697dcd0ad6f16c1b5de80f1df2e02353e3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 11:11:54 +0100 Subject: [PATCH 206/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 79540bceb..7beaf8215 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 81161616a..b737a609d 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 69646ea3e..47f3ca60b 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 81e8b81a4..f2293656b 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 9b3e9e462..a750aeda1 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.0 + 4.3.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 97f27ffb9..fc4967bfd 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.0 + 4.3.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 032e5d591..d81513c7d 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index f82ee9aaa..08c0c8183 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 21abe34d4..bf2438b79 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 9217b8092..f95d18e22 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.0 + 4.3.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.3.0 + HEAD From 3e2ee064711b11bb47dd04a9de4121cea6ed14cd Mon Sep 17 00:00:00 2001 From: sbonnet Date: Fri, 3 May 2024 10:50:29 +0200 Subject: [PATCH 207/234] Remove calendar dates (not only calendars) --- .../impl/RemoveOldCalendarStatements.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java index 5fa491838..38f369b69 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatements.java @@ -20,6 +20,7 @@ import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.gtfs.model.ServiceCalendar; +import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; import org.onebusaway.gtfs_transformer.services.TransformContext; @@ -64,5 +65,16 @@ public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfs for (ServiceCalendar serviceCalendar : serviceCalendarsToRemove) { removeEntityLibrary.removeCalendar(gtfsMutableRelationalDao, serviceCalendar.getServiceId()); } + + Set serviceCalendarDatesToRemove = new HashSet(); + for (ServiceCalendarDate calendarDate : gtfsMutableRelationalDao.getAllCalendarDates()) { + if (calendarDate.getDate().getAsDate().before(today)) { + serviceCalendarDatesToRemove.add(calendarDate); + } + } + for (ServiceCalendarDate serviceCalendarDate : serviceCalendarDatesToRemove) { + // here we can't delete the trips as the serviceid may be active elsewhere + removeEntityLibrary.removeServiceCalendarDate(gtfsMutableRelationalDao, serviceCalendarDate); + } } } \ No newline at end of file From 0b6f2db9ef3847f03850ad5ce730c092b61b18a5 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Fri, 6 Dec 2024 16:13:29 +0100 Subject: [PATCH 208/234] Add test file and document remove old calendar dates operation --- docs/onebusaway-gtfs-transformer-cli.md | 17 +++++++++++++---- .../impl/RemoveOldCalendarStatementsTest.java | 11 +++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index 928734366..ec386d417 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -343,13 +343,13 @@ those manually. #### Remove Old Calendar Statements -RemoveOldCalendarStatements is an operation designed to remove calendar entries that are no longer valid on today's date. +RemoveOldCalendarStatements is an operation designed to remove calendar and calendar dates entries that are no longer valid on today's date. -By default, it deletes entries from the calendar.txt file whose end_date field has passed. +By default, it deletes entries from both the calendar.txt and calendar_dates.txt files, where the end_date in calendar.txt or the date field in calendar_dates.txt has passed. -With the remove_today attribute added to the JSON transformer snippet, users can control whether calendar entries valid for today are included or excluded in the output GTFS. +With the remove_today attribute added to the JSON transformer snippet, users can control whether entries in calendar or calendar_dates that are valid for today are included or excluded in the GTFS output. - * If remove_today is set to true, the transformer will remove the calendar entries for the current date. + * If remove_today is set to true, the transformer will remove entries for the current date. ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":true} @@ -361,6 +361,15 @@ With the remove_today attribute added to the JSON transformer snippet, users can {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":false} ``` +Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. + +Without this retain operation, the `trips.txt` file will contain trips with non-existent calendar dates, leading to invalid data. + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":false} +{"op":"retain", "match":{"file":"calendar_dates.txt"}, "retainBlocks":false} +``` + #### Deduplicate Calendar Entries Finds GTFS service_ids that have the exact same set of active days and consolidates each set of duplicated diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java index 3519bfc26..87a2d3937 100644 --- a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RemoveOldCalendarStatementsTest.java @@ -31,11 +31,18 @@ public void setup() throws IOException{ String startDate = getCurrentDateFormatted(-3); String endDate = getCurrentDateFormatted(null); + // Define additional date for testing purposes, relative to startDate + String threeDaysFromStartDate = getCurrentDateFormatted(3); + _gtfs.putCalendars( 1, "start_date="+startDate, "end_date="+endDate ); + + // Insert calendar dates entries + _gtfs.putCalendarDates("sid0="+startDate+","+endDate+","+ + threeDaysFromStartDate); } @Test @@ -46,6 +53,8 @@ public void testRemoveCalendarForToday() throws IOException { removeOldCalendarStatements.run(_context, dao); // Verify that GtfsMutableRelationalDao object no longer contains any calendar entries after removing the calendar for today's date assertEquals(0,dao.getAllCalendars().size()); + // Verify that GtfsMutableRelationalDao object no longer contains any calendar dates entries after removing invalid dates, including today's date + assertEquals(0,dao.getAllCalendarDates().size()); } @Test @@ -55,6 +64,8 @@ public void testRemoveCalendar() throws IOException { removeOldCalendarStatements.run(_context, dao); // Verify that GtfsMutableRelationalDao object still contain the initially added calendar entry assertEquals(1,dao.getAllCalendars().size()); + // Verify that GtfsMutableRelationalDao object contains two calendar dates entries after removing invalid dates + assertEquals(2,dao.getAllCalendarDates().size()); } // Helper function to get today's date in the required format From c6aaaecd035ea3f59a51aad2f04c700a1e2e320a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 10:31:32 +0100 Subject: [PATCH 209/234] [maven-release-plugin] prepare release v4.3.1 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 7beaf8215..6ca7ce1f1 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index b737a609d..1a7e629bd 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 47f3ca60b..49cda30a8 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index f2293656b..ce7980e16 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index a750aeda1..6fe22b8ea 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.1-SNAPSHOT + 4.3.1 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index fc4967bfd..f000b0736 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.1-SNAPSHOT + 4.3.1 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index d81513c7d..b79f45e7c 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 08c0c8183..278c0f7b9 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index bf2438b79..57dc77b42 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 ../pom.xml diff --git a/pom.xml b/pom.xml index f95d18e22..b4529cccf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1-SNAPSHOT + 4.3.1 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.3.1 From 487985709db9b2cd4c76c61f5e9d268d6374efc1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 10:31:34 +0100 Subject: [PATCH 210/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 6ca7ce1f1..bd3f0b682 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 1a7e629bd..0541b8dfb 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 49cda30a8..5ad68d25b 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index ce7980e16..9dd1ed3f2 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 6fe22b8ea..1dbf12a65 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.1 + 4.3.2-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index f000b0736..72298ca97 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.1 + 4.3.2-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index b79f45e7c..5ea909130 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 278c0f7b9..dfbfa636a 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 57dc77b42..bb8d00b7a 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index b4529cccf..3313cbd9a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.1 + 4.3.2-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.3.1 + HEAD From f76b247e863721f365d02b7afb897cb3f46958f2 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Mon, 13 May 2024 10:41:44 +0200 Subject: [PATCH 211/234] create new transform operation RemoveStopsOutsidePolygone: remove stops outside polygone/multiPolygone --- onebusaway-gtfs-transformer/pom.xml | 5 + .../impl/RemoveStopsOutsidePolygone.java | 99 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 08c0c8183..204f61a74 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -77,6 +77,11 @@ com.sun.xml.bind jaxb-impl + + org.locationtech.jts + jts-core + 1.19.0 + diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java new file mode 100644 index 000000000..ecbf39a66 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java @@ -0,0 +1,99 @@ +package org.onebusaway.gtfs_transformer.impl; + +import java.util.ArrayList; +import java.util.List; + +import java.io.Serializable; + +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.geom.*; + +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.gtfs.model.IdentityBean; +import org.onebusaway.gtfs.model.Stop; +import org.onebusaway.gtfs.serialization.GtfsEntitySchemaFactory; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs_transformer.factory.EntityRetentionGraph; +import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; +import org.onebusaway.gtfs_transformer.services.TransformContext; +import org.slf4j.Logger; + +import org.slf4j.LoggerFactory; + +public class RemoveStopsOutsidePolygone implements GtfsTransformStrategy { + private final Logger log = LoggerFactory.getLogger(RemoveStopsOutsidePolygone.class); + + @CsvField(optional = true) + private String polygone; + + public void setPolygone(String polygone) { + this.polygone = polygone; + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + /* + * example: + * {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RemoveStopsOutsidePolygone","polygone":wkt_polygone ..."} + */ + + @Override + public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { + Geometry geometry = buildPolygone(polygone); + EntityRetentionGraph graph = new EntityRetentionGraph(gtfsMutableRelationalDao); + graph.setRetainBlocks(false); + // browse all stops and retain only those inside polygone/multiPolygone + if (geometry.isValid() && !geometry.isEmpty()){ + for (Stop stop : gtfsMutableRelationalDao.getAllStops()) { + if (insidePolygon(geometry,stop.getLon(),stop.getLat())){ + graph.retain(stop, true); + } + } + } + + // remove non retained objects + for (Class entityClass : GtfsEntitySchemaFactory.getEntityClasses()) { + List objectsToRemove = new ArrayList(); + for (Object entity : gtfsMutableRelationalDao.getAllEntitiesForType(entityClass)) { + if (!graph.isRetained(entity)){ + objectsToRemove.add(entity); + } + } + for (Object toRemove : objectsToRemove){ + gtfsMutableRelationalDao.removeEntity((IdentityBean) toRemove); + } + } + } + + /* + * create polygone/multiPolygone from 'polygone' variable in json file + * return Geometry variable + * return null if an exception is encountered when parsing the wkt string + */ + private Geometry buildPolygone(String wktPolygone) { + WKTReader reader = new WKTReader(); + try{ + return reader.read(wktPolygone); + } catch (ParseException e){ + String message = String.format("Error parsing WKT string : %s", e.getMessage()); + log.error(message); + return null; + } + + } + /* + * insidePolygone returns boolean variable + * true: if polygone contains point + * false if point is outside polygone + */ + private boolean insidePolygon(Geometry geometry, double lon, double lat) { + GeometryFactory geometryFactory = new GeometryFactory(); + Point point = geometryFactory.createPoint(new Coordinate(lon, lat)); + return geometry.contains(point); + } + +} From 333f677903428919ab9560a920a948048aa54f99 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Tue, 10 Dec 2024 13:35:13 +0100 Subject: [PATCH 212/234] Retain up from polygon, add test file and document strategy --- docs/onebusaway-gtfs-transformer-cli.md | 21 +++++ ...Polygone.java => RetainUpFromPolygon.java} | 35 ++++----- .../impl/RetainUpFromPolygonTest.java | 76 +++++++++++++++++++ 3 files changed, 112 insertions(+), 20 deletions(-) rename onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/{RemoveStopsOutsidePolygone.java => RetainUpFromPolygon.java} (76%) create mode 100644 onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygonTest.java diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index ec386d417..c35164f5c 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -20,6 +20,7 @@ * [Path Expressions](#path-expressions-) * [Retain an Entity](#retain-an-entity) * [Remove an Entity](#remove-an-entity) + * [Retain Up From Polygon](#retain-up-from-polygon) * [Trim a Trip](#trim-a-trip) * [Generate Stop Times](#generate-stop-times) * [Extend Service Calendars](#extend-service-calendars) @@ -270,6 +271,26 @@ You can remove a specific entity from a feed. Note that removing an entity has a cascading effect. If you remove a trip, all the stop times that depend on that trip will also be removed. If you remove a route, all the trips and stop times for that route will be removed. + +#### Retain Up From Polygon + +Retain Up From Polygon is an operation that filters GTFS input data based on a specified geographic area, using a polygon defined in WKT (Well-Known Text) format, which is configurable in the JSON transformer snippet. + +This strategy applies two main functions: + + * **Retain Function**: retains **up** all stops, trips, and routes that are located inside the defined polygon, then the algorithm automatically applies a retain **down** to these entities. + + * **Remove Function**: any entities not retained within the polygon are removed. + +This strategy ensures that the GTFS output only contains data relevant to the geographical area concerned. + +**Parameters**: + + * **polygon**: a required argument, which accepts the polygon in WKT format using the WGS84 coordinate system (SRID: 4326). This polygon defines the area of interest for filtering. + +``` +{"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RetainUpFromPolygon","polygon":"POLYGON ((-123.0 37.0, -123.0 38.0, -122.0 38.0, -122.0 37.0, -123.0 37.0))"} +``` #### Trim a Trip diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java similarity index 76% rename from onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java rename to onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java index ecbf39a66..0ab4b62ab 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveStopsOutsidePolygone.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java @@ -21,14 +21,14 @@ import org.slf4j.LoggerFactory; -public class RemoveStopsOutsidePolygone implements GtfsTransformStrategy { - private final Logger log = LoggerFactory.getLogger(RemoveStopsOutsidePolygone.class); +public class RetainUpFromPolygon implements GtfsTransformStrategy { + private final Logger log = LoggerFactory.getLogger(RetainUpFromPolygon.class); - @CsvField(optional = true) - private String polygone; + @CsvField(optional = false) + private String polygon; - public void setPolygone(String polygone) { - this.polygone = polygone; + public void setPolygon(String polygon) { + this.polygon = polygon; } @Override @@ -36,17 +36,12 @@ public String getName() { return this.getClass().getSimpleName(); } - /* - * example: - * {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RemoveStopsOutsidePolygone","polygone":wkt_polygone ..."} - */ - - @Override + @Override public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { - Geometry geometry = buildPolygone(polygone); + Geometry geometry = buildPolygon(polygon); EntityRetentionGraph graph = new EntityRetentionGraph(gtfsMutableRelationalDao); graph.setRetainBlocks(false); - // browse all stops and retain only those inside polygone/multiPolygone + // browse all stops and retain only those inside polygon/multipolygon if (geometry.isValid() && !geometry.isEmpty()){ for (Stop stop : gtfsMutableRelationalDao.getAllStops()) { if (insidePolygon(geometry,stop.getLon(),stop.getLat())){ @@ -70,14 +65,14 @@ public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfs } /* - * create polygone/multiPolygone from 'polygone' variable in json file + * create polygon/multiPolygon from 'polygon' variable in json file * return Geometry variable * return null if an exception is encountered when parsing the wkt string */ - private Geometry buildPolygone(String wktPolygone) { + private Geometry buildPolygon(String polygonWKT) { WKTReader reader = new WKTReader(); try{ - return reader.read(wktPolygone); + return reader.read(polygonWKT); } catch (ParseException e){ String message = String.format("Error parsing WKT string : %s", e.getMessage()); log.error(message); @@ -86,9 +81,9 @@ private Geometry buildPolygone(String wktPolygone) { } /* - * insidePolygone returns boolean variable - * true: if polygone contains point - * false if point is outside polygone + * insidePolygon returns boolean variable + * true: if polygon contains point + * false if point is outside polygon */ private boolean insidePolygon(Geometry geometry, double lon, double lat) { GeometryFactory geometryFactory = new GeometryFactory(); diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygonTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygonTest.java new file mode 100644 index 000000000..a00febd01 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygonTest.java @@ -0,0 +1,76 @@ +package org.onebusaway.gtfs_transformer.impl; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs.services.MockGtfs; +import org.onebusaway.gtfs_transformer.services.TransformContext; + +public class RetainUpFromPolygonTest { + + private RetainUpFromPolygon retainUpFromPolygon = new RetainUpFromPolygon(); + private TransformContext _context = new TransformContext(); + private MockGtfs _gtfs; + + @BeforeEach + public void setup() throws IOException{ + + _gtfs = MockGtfs.create(); + // Insert mock data into the GTFS for testing: + // 1 agency + _gtfs.putAgencies(1); + // 4 routes + _gtfs.putRoutes(4); + // 4 trips + _gtfs.putTrips(4, "r$0","sid$0"); + // 8 stops + _gtfs.putStops(8); + // 13 stop times + _gtfs.putLines("stop_times.txt", + "trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,shape_dist_traveled", + // Trip t0: sequence of stops s0,s1,s2,s3 + "t0,08:00:00,08:25:00,s0,0,,,,", + "t0,08:30:00,08:55:00,s1,1,,,,", + "t0,09:00:00,09:55:00,s2,2,,,,", + "t0,10:00:00,10:30:00,s3,3,,,,", + // Trip t1: reverse sequence of stops s3,s2,s1,s0 + "t1,08:00:00,08:25:00,s3,0,,,,", + "t1,08:30:00,08:55:00,s2,1,,,,", + "t1,09:00:00,09:55:00,s1,2,,,,", + "t1,10:00:00,10:00:00,s0,3,,,,", + // Trip t2: sequence of stops s3,s4,s5 + "t2,10:00:00,10:55:00,s3,0,,,,", + "t2,11:00:00,11:25:00,s4,1,,,,", + "t2,11:30:00,11:55:00,s5,2,,,,", + // Trip t3: Additional stops + "t3,12:00:00,12:25:00,s6,0,,,,", + "t3,12:30:00,12:55:00,s7,1,,,,"); + } + + @Test + public void testRetainUpFromPolygonTest() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + + // Define a polygon in WKT (Well-Known Text) format + // This polygon is designed to include only the first 4 stops (S0 to S4) + String polygonWKT = "POLYGON ((-122.308 47.653, -122.308 47.666, -122.307 47.666, -122.307 47.665, -122.307 47.661, -122.307 47.657, -122.307 47.653, -122.308 47.653))"; + retainUpFromPolygon.setPolygon(polygonWKT); + + // Execute the retainUpFromPolygon strategy based on the polygon + retainUpFromPolygon.run(_context, dao); + + // Verify that the number of routes is reduced to 3 + assertEquals(3,dao.getAllRoutes().size()); + + // Verify that the number of trips is reduced to 3 + assertEquals(3,dao.getAllTrips().size()); + + // Verify that the number of stops is reduced to 6 + assertEquals(6,dao.getAllStops().size()); + + // Verify that the number of stop times is reduced to 11 + assertEquals(11,dao.getAllStopTimes().size()); + } +} \ No newline at end of file From af9cb26a7db0278ec92965e2f9609462917766a8 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Thu, 12 Dec 2024 11:06:03 +0100 Subject: [PATCH 213/234] Change buildPolygon and the check function into the setter --- .../impl/RetainUpFromPolygon.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java index 0ab4b62ab..d7adfb524 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java @@ -17,18 +17,22 @@ import org.onebusaway.gtfs_transformer.factory.EntityRetentionGraph; import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; import org.onebusaway.gtfs_transformer.services.TransformContext; -import org.slf4j.Logger; - -import org.slf4j.LoggerFactory; public class RetainUpFromPolygon implements GtfsTransformStrategy { - private final Logger log = LoggerFactory.getLogger(RetainUpFromPolygon.class); @CsvField(optional = false) private String polygon; + @CsvField(ignore = true) + private Geometry polygonGeometry; + public void setPolygon(String polygon) { this.polygon = polygon; + this.polygonGeometry = buildPolygon(polygon); + + if (this.polygonGeometry == null || !this.polygonGeometry.isValid() || this.polygonGeometry.isEmpty()) { + throw new IllegalArgumentException("The provided polygon is invalid or empty."); + } } @Override @@ -38,15 +42,12 @@ public String getName() { @Override public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { - Geometry geometry = buildPolygon(polygon); EntityRetentionGraph graph = new EntityRetentionGraph(gtfsMutableRelationalDao); graph.setRetainBlocks(false); // browse all stops and retain only those inside polygon/multipolygon - if (geometry.isValid() && !geometry.isEmpty()){ - for (Stop stop : gtfsMutableRelationalDao.getAllStops()) { - if (insidePolygon(geometry,stop.getLon(),stop.getLat())){ - graph.retain(stop, true); - } + for (Stop stop : gtfsMutableRelationalDao.getAllStops()) { + if (insidePolygon(polygonGeometry,stop.getLon(),stop.getLat())){ + graph.retain(stop, true); } } @@ -65,25 +66,29 @@ public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfs } /* - * create polygon/multiPolygon from 'polygon' variable in json file - * return Geometry variable - * return null if an exception is encountered when parsing the wkt string + * Creates a Geometry object (polygon or multi-polygon) from the provided WKT string. + * + * @param polygonWKT The WKT representation of the polygon. + * @return The Geometry object. + * @throws IllegalArgumentException if the WKT string is invalid or cannot be parsed. */ private Geometry buildPolygon(String polygonWKT) { WKTReader reader = new WKTReader(); try{ return reader.read(polygonWKT); } catch (ParseException e){ - String message = String.format("Error parsing WKT string : %s", e.getMessage()); - log.error(message); - return null; + throw new IllegalArgumentException( + String.format("Error parsing WKT string: %s", e.getMessage()), e + ); } - } /* - * insidePolygon returns boolean variable - * true: if polygon contains point - * false if point is outside polygon + * insidePolygon Checks whether a given point (specified by its longitude and latitude) is inside a given polygon or multipolygon. + * + * @param geometry The Geometry object representing the polygon or multipolygon. + * @param lon the longitude of the point to check. + * @param lat the latitude of the point to check. + * @return true if the point is within the boundaries of the geometry; false otherwise. */ private boolean insidePolygon(Geometry geometry, double lon, double lat) { GeometryFactory geometryFactory = new GeometryFactory(); @@ -91,4 +96,4 @@ private boolean insidePolygon(Geometry geometry, double lon, double lat) { return geometry.contains(point); } -} +} \ No newline at end of file From c573cfa454e643e788141059116e9e8bf6bb9632 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Thu, 12 Dec 2024 13:44:12 +0100 Subject: [PATCH 214/234] Update documentation for RetainUpFromPolygon strategy with detailed retainUp and retainDown process --- docs/onebusaway-gtfs-transformer-cli.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index c35164f5c..efe4c6cc9 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -278,11 +278,15 @@ Retain Up From Polygon is an operation that filters GTFS input data based on a s This strategy applies two main functions: - * **Retain Function**: retains **up** all stops, trips, and routes that are located inside the defined polygon, then the algorithm automatically applies a retain **down** to these entities. + * **Retain Function**: retains **up** all stops, trips, and routes that are located inside the defined polygon. + + The algorithm starts by applying retain up to each entity, traversing the entity dependency tree. Starting from the stop, retain up is applied to the stop_times referencing this stop, then to the trips, and so on. + + Once the base of the entity tree is reached, it automatically applies retain **down** to all the traversed entities. Therefore, all the trips of the route and then all the stop_times of each trip will be tagged as **retain**. * **Remove Function**: any entities not retained within the polygon are removed. -This strategy ensures that the GTFS output only contains data relevant to the geographical area concerned. +This strategy ensures that the GTFS output retains only the entities directly or indirectly linked to the geographical area concerned. **Parameters**: From be01cacc80333bcaa946230ef8ab383fe8952f4e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:54:09 +0000 Subject: [PATCH 215/234] Update dependency org.locationtech.jts:jts-core to v1.20.0 --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index a5b18a6b4..e5cf3bd66 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -80,7 +80,7 @@ org.locationtech.jts jts-core - 1.19.0 + 1.20.0 From 7097105770c6b539f6180e3c731dd99c21a33e78 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Dec 2024 13:57:46 +0100 Subject: [PATCH 216/234] Make some instances reusable --- .../gtfs_transformer/impl/RetainUpFromPolygon.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java index d7adfb524..08945a3de 100644 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RetainUpFromPolygon.java @@ -20,6 +20,9 @@ public class RetainUpFromPolygon implements GtfsTransformStrategy { + private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(); + private final WKTReader wktReader = new WKTReader(GEOMETRY_FACTORY); + @CsvField(optional = false) private String polygon; @@ -73,9 +76,8 @@ public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfs * @throws IllegalArgumentException if the WKT string is invalid or cannot be parsed. */ private Geometry buildPolygon(String polygonWKT) { - WKTReader reader = new WKTReader(); try{ - return reader.read(polygonWKT); + return wktReader.read(polygonWKT); } catch (ParseException e){ throw new IllegalArgumentException( String.format("Error parsing WKT string: %s", e.getMessage()), e @@ -91,8 +93,7 @@ private Geometry buildPolygon(String polygonWKT) { * @return true if the point is within the boundaries of the geometry; false otherwise. */ private boolean insidePolygon(Geometry geometry, double lon, double lat) { - GeometryFactory geometryFactory = new GeometryFactory(); - Point point = geometryFactory.createPoint(new Coordinate(lon, lat)); + Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)); return geometry.contains(point); } From 9fcb54e9bcadbde9b65e4d85d0e3f9877d2c9688 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Dec 2024 14:04:38 +0100 Subject: [PATCH 217/234] [maven-release-plugin] prepare release v4.4.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index bd3f0b682..093f350bc 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 0541b8dfb..ccb9dad5c 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 5ad68d25b..5c78758f1 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 9dd1ed3f2..ec11e97bb 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 1dbf12a65..1c54834a9 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.2-SNAPSHOT + 4.4.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 72298ca97..9cd6fc9a3 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.3.2-SNAPSHOT + 4.4.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index 5ea909130..e0b2c3c45 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index e5cf3bd66..0e6152502 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index bb8d00b7a..0a3904d71 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 3313cbd9a..b74c97572 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.3.2-SNAPSHOT + 4.4.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.4.0 From 2880bb2f280d19f9d12b59dbb20b23f2acfb6577 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Dec 2024 14:04:43 +0100 Subject: [PATCH 218/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 093f350bc..f18277f8b 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index ccb9dad5c..c1e07c9e3 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 5c78758f1..8f1919df5 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index ec11e97bb..c3dc8b806 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 1c54834a9..623f125a8 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.4.0 + 4.4.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 9cd6fc9a3..a32413b72 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.4.0 + 4.4.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index e0b2c3c45..aea0799e3 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 0e6152502..53ef4d7c5 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 0a3904d71..f80ddaeff 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index b74c97572..e75ac0439 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.0 + 4.4.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.4.0 + HEAD From 513678ac6cc07af4a0a885f5e6f98bb3c35b8352 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:00:07 +0000 Subject: [PATCH 219/234] Update Test dependencies (#312) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e75ac0439..bc27412dc 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ UTF-8 2.0.16 0.0.13 - 5.11.3 + 5.11.4 @@ -193,7 +193,7 @@ me.fabriciorby maven-surefire-junit5-tree-reporter - 1.3.0 + 1.4.0 From 173dea196965b8e4825663970a13862dba340cb0 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Tue, 14 May 2024 17:55:13 +0200 Subject: [PATCH 220/234] TrimTripsFromPolygon: add strategy --- .../impl/RemoveEntitiesOutsidePolygone.java | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java new file mode 100644 index 000000000..f1ef46c87 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java @@ -0,0 +1,187 @@ +/** + * Copyright (C) 2024 Tisseo Voyageurs. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onebusaway.gtfs_transformer.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.geom.*; + +import org.onebusaway.collections.beans.PropertyPathExpression; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.gtfs.model.Stop; +import org.onebusaway.gtfs.model.StopTime; +import org.onebusaway.gtfs.model.Trip; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; +import org.onebusaway.gtfs_transformer.services.TransformContext; +import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy; +import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy.TrimOperation; +import org.onebusaway.gtfs_transformer.match.PropertyValueEntityMatch; +import org.onebusaway.gtfs_transformer.match.SimpleValueMatcher; +import org.onebusaway.gtfs_transformer.match.TypedEntityMatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* + * remove entities outside polygone geometry + * remove procedre is based on the trim_trip strategy + * providing the trip_id key and the parameters from_stop_id/to_stop_id + * example: + * {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RemoveEntitiesOutsidePolygone","polygone": wkt_polygone ...} + */ + +public class RemoveEntitiesOutsidePolygone implements GtfsTransformStrategy { + private final Logger _log = LoggerFactory.getLogger(RemoveEntitiesOutsidePolygone.class); + + // retrieves string polygone from config file + @CsvField(optional = true) + private String polygone; + + public void setPolygone(String polygone) { + this.polygone = polygone; + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { + + Geometry geometry = buildPolygone(polygone); + TrimTripTransformStrategy strategy = new TrimTripTransformStrategy(); + TransformContext context = new TransformContext(); + Map> tripsWithStopsOutsidePolygone = new HashMap<>(); + + // browse all trips + for (Trip trip : gtfsMutableRelationalDao.getAllTrips()){ + for (StopTime stopTime: gtfsMutableRelationalDao.getStopTimesForTrip(trip)){ + + Stop stop = gtfsMutableRelationalDao.getStopForId(stopTime.getStop().getId()); + if (! insidePolygon(geometry,stop.getLon(),stop.getLat())){ + List stopsWithSequence = new ArrayList<>(); + Integer totalStopTimes = gtfsMutableRelationalDao.getStopTimesForTrip(trip).size(); + // append in List + stopsWithSequence.add(new Object[] {stopTime.getStopSequence(), stop.getId().getId(),totalStopTimes}); + // update dictionary if stop is outside polygone + updateDict(tripsWithStopsOutsidePolygone,trip.getId(),stopsWithSequence); + } + } + } + // browse Map + for (Map.Entry> entry: tripsWithStopsOutsidePolygone.entrySet()){ + + TrimOperation operation = new TrimOperation(); + + Boolean trimFrom = false; + Boolean trimTo = false; + Integer indexFromStopId = 0; + Integer indexToStopId = 0; + Integer allStopTimes = 0; + String fromStopId = null; + String toStopId = null; + Object firstObject = entry.getValue().get(0); + Object[] firstStop = (Object[]) firstObject; + Object[] lastStop = null; + + // get from stop id : fromStopId value, indexFromStopId + if (firstStop.length > 0){ + fromStopId = (String) firstStop[1]; // get fromStopId + indexFromStopId = (Integer) firstStop[0]; + allStopTimes = (Integer) firstStop[2] - 1; + } + + // get to stop id : toStopId value, indexToStopIds + if (entry.getValue().size() > 1) { + Object lastObject = entry.getValue().get(entry.getValue().size() - 1); + lastStop = (Object[]) lastObject; + toStopId = (String) lastStop[1]; + indexToStopId = (Integer) lastStop[0]; + } + + // check whether trimFrom or trimTo is used in the trim operation + if (indexFromStopId != null && indexFromStopId > 0){ + trimFrom = true; + } + else if (indexToStopId > 0 && indexToStopId < allStopTimes){ + trimTo = true; + } + // operation object to used it int trim trip strategy + PropertyPathExpression expression = new PropertyPathExpression("id"); + operation.setMatch(new TypedEntityMatch(Trip.class, + new PropertyValueEntityMatch(expression, + new SimpleValueMatcher(entry.getKey())))); + + if (Boolean.TRUE.equals(trimFrom)) + operation.setFromStopId(fromStopId); + if (Boolean.TRUE.equals(trimTo)) + operation.setToStopId(toStopId); + + // add operation + execute strategy + strategy.addOperation(operation); + strategy.run(context, gtfsMutableRelationalDao); + } + } + + /* + * updateDict : function used to add/update dictionary + */ + private static void updateDict(Map> dictionary, Object key, List values) { + // If the key already exists, append the values to its existing list + if (dictionary.containsKey(key)) { + List existingValues = dictionary.get(key); + existingValues.addAll(values); + } + // If the key is new, add it to the dictionary with the list of values + else { + dictionary.put(key, values); + } + } + + /* + * create polygone/multiPolygone from polygone variable in json file + * return Geometry variable + * return null if an exception is encountered when parsing the wkt string + */ + private Geometry buildPolygone(String wktPolygone) { + WKTReader reader = new WKTReader(); + try{ + return reader.read(wktPolygone); + } catch (ParseException e){ + String message = String.format("Error parsing WKT string : %s", e.getMessage()); + _log.error(message); + return null; + } + + } + /* + * insidePolygone returns boolean variable + * true: if polygone contains point + * false if point is outside polygone + */ + private boolean insidePolygon(Geometry geometry, double lon, double lat) { + GeometryFactory geometryFactory = new GeometryFactory(); + Point point = geometryFactory.createPoint(new Coordinate(lon, lat)); + return geometry.contains(point); + } + +} From 84d0b1459fdb2ca0fda75dc476d7ba8f139de978 Mon Sep 17 00:00:00 2001 From: Seif Eddine GHAZOUANI Date: Fri, 13 Dec 2024 15:47:52 +0100 Subject: [PATCH 221/234] Trim Trip From Polygon, add test file and document strategy --- docs/onebusaway-gtfs-transformer-cli.md | 39 +++- .../impl/RemoveEntitiesOutsidePolygone.java | 187 ------------------ .../impl/TrimTripFromPolygon.java | 138 +++++++++++++ .../impl/TrimTripFromPolygonTest.java | 81 ++++++++ 4 files changed, 257 insertions(+), 188 deletions(-) delete mode 100644 onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java create mode 100644 onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygon.java create mode 100644 onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygonTest.java diff --git a/docs/onebusaway-gtfs-transformer-cli.md b/docs/onebusaway-gtfs-transformer-cli.md index efe4c6cc9..666f4949b 100644 --- a/docs/onebusaway-gtfs-transformer-cli.md +++ b/docs/onebusaway-gtfs-transformer-cli.md @@ -21,6 +21,7 @@ * [Retain an Entity](#retain-an-entity) * [Remove an Entity](#remove-an-entity) * [Retain Up From Polygon](#retain-up-from-polygon) + * [Trim Trip From Polygon](#trim-trip-from-polygon) * [Trim a Trip](#trim-a-trip) * [Generate Stop Times](#generate-stop-times) * [Extend Service Calendars](#extend-service-calendars) @@ -31,6 +32,7 @@ * [Shift Negative Stop Times](#shift-negative-stop-times) * [Arbitrary Transform](#arbitrary-transform) * [How to Reduce your GTFS](#how-to-reduce-your-gtfs) + * [Cleap National GTFS for Regional Integration and Consistency](#cleap-national-gtfs-for-regional-integration-and-consistency) ## Introduction @@ -295,6 +297,22 @@ This strategy ensures that the GTFS output retains only the entities directly or ``` {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RetainUpFromPolygon","polygon":"POLYGON ((-123.0 37.0, -123.0 38.0, -122.0 38.0, -122.0 37.0, -123.0 37.0))"} ``` + +#### Trim Trip From Polygon + +The Trim Trip From Polygon strategy refines GTFS data by removing all stop_times associated with stops located outside a specified geographical area. The area is defined using a configurable WKT Polygon or Multipolygon in the JSON transformer snippet. + +This removal of stop_times is achieved by invoking the **TrimTrip operation**, ensuring that only stops within the defined polygon are retained. + +Only valid stop_times within the polygon are retained, maintaining the integrity of the trips. + +**Parameters**: + + * **polygon**: a required argument, which accepts the polygon in WKT format using the WGS84 coordinate system (SRID: 4326). This polygon defines the area of interest for filtering. + +``` +{"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.TrimTripFromPolygon","polygon":"POLYGON ((-123.0 37.0, -123.0 38.0, -122.0 38.0, -122.0 37.0, -123.0 37.0))"} +``` #### Trim a Trip @@ -595,4 +613,23 @@ and frequency-based service, using the transform. This can be handy to add temp {"op":"stop_times_factory", "trip_id":"t1", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["s3", "s2", "s1", "s0"]} ``` - +### Clean National GTFS for Regional Integration and Consistency + +To prepare for the integration of the national GTFS with our regional GTFS, several transformations have benn applied to the national GTFS in order to clean up and adapt the data to regional requirements. Below is an overview of the operations carried out: + + * Removing Inactive Calendars and Dates. + * Truncating Calendars and Dates to 21 days. + * Retaining Data Within a Specific Geographic Area: a smaller geographic area was used for retaining only the entities within our area of interest. + * Trimming Stop Times Outside a Specific Geographic Area: a larger polygon was used to ensure that only the relevant stops_times within a wider region are retained. + +``` +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements"} + +{"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} + +{"op":"retain", "match":{"file":"calendar_dates.txt"}, "retainBlocks":false} + +{"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RetainUpFromPolygon","polygon":"MULTIPOLYGON (((1.2 43.7, 1.55 43.7, 1.55 43.4, 1.2 43.4, 1.2 43.7)))"} + +{"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.TrimTripFromPolygon","polygon":"MULTIPOLYGON (((1.0 44.2, 2.2 44.2, 2.2 43.3, 1.0 43.3, 1.0 44.2)))"} +``` \ No newline at end of file diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java deleted file mode 100644 index f1ef46c87..000000000 --- a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/RemoveEntitiesOutsidePolygone.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright (C) 2024 Tisseo Voyageurs. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onebusaway.gtfs_transformer.impl; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.locationtech.jts.io.ParseException; -import org.locationtech.jts.io.WKTReader; -import org.locationtech.jts.geom.*; - -import org.onebusaway.collections.beans.PropertyPathExpression; -import org.onebusaway.csv_entities.schema.annotations.CsvField; -import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.StopTime; -import org.onebusaway.gtfs.model.Trip; -import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; -import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; -import org.onebusaway.gtfs_transformer.services.TransformContext; -import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy; -import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy.TrimOperation; -import org.onebusaway.gtfs_transformer.match.PropertyValueEntityMatch; -import org.onebusaway.gtfs_transformer.match.SimpleValueMatcher; -import org.onebusaway.gtfs_transformer.match.TypedEntityMatch; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/* - * remove entities outside polygone geometry - * remove procedre is based on the trim_trip strategy - * providing the trip_id key and the parameters from_stop_id/to_stop_id - * example: - * {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RemoveEntitiesOutsidePolygone","polygone": wkt_polygone ...} - */ - -public class RemoveEntitiesOutsidePolygone implements GtfsTransformStrategy { - private final Logger _log = LoggerFactory.getLogger(RemoveEntitiesOutsidePolygone.class); - - // retrieves string polygone from config file - @CsvField(optional = true) - private String polygone; - - public void setPolygone(String polygone) { - this.polygone = polygone; - } - - @Override - public String getName() { - return this.getClass().getSimpleName(); - } - - @Override - public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { - - Geometry geometry = buildPolygone(polygone); - TrimTripTransformStrategy strategy = new TrimTripTransformStrategy(); - TransformContext context = new TransformContext(); - Map> tripsWithStopsOutsidePolygone = new HashMap<>(); - - // browse all trips - for (Trip trip : gtfsMutableRelationalDao.getAllTrips()){ - for (StopTime stopTime: gtfsMutableRelationalDao.getStopTimesForTrip(trip)){ - - Stop stop = gtfsMutableRelationalDao.getStopForId(stopTime.getStop().getId()); - if (! insidePolygon(geometry,stop.getLon(),stop.getLat())){ - List stopsWithSequence = new ArrayList<>(); - Integer totalStopTimes = gtfsMutableRelationalDao.getStopTimesForTrip(trip).size(); - // append in List - stopsWithSequence.add(new Object[] {stopTime.getStopSequence(), stop.getId().getId(),totalStopTimes}); - // update dictionary if stop is outside polygone - updateDict(tripsWithStopsOutsidePolygone,trip.getId(),stopsWithSequence); - } - } - } - // browse Map - for (Map.Entry> entry: tripsWithStopsOutsidePolygone.entrySet()){ - - TrimOperation operation = new TrimOperation(); - - Boolean trimFrom = false; - Boolean trimTo = false; - Integer indexFromStopId = 0; - Integer indexToStopId = 0; - Integer allStopTimes = 0; - String fromStopId = null; - String toStopId = null; - Object firstObject = entry.getValue().get(0); - Object[] firstStop = (Object[]) firstObject; - Object[] lastStop = null; - - // get from stop id : fromStopId value, indexFromStopId - if (firstStop.length > 0){ - fromStopId = (String) firstStop[1]; // get fromStopId - indexFromStopId = (Integer) firstStop[0]; - allStopTimes = (Integer) firstStop[2] - 1; - } - - // get to stop id : toStopId value, indexToStopIds - if (entry.getValue().size() > 1) { - Object lastObject = entry.getValue().get(entry.getValue().size() - 1); - lastStop = (Object[]) lastObject; - toStopId = (String) lastStop[1]; - indexToStopId = (Integer) lastStop[0]; - } - - // check whether trimFrom or trimTo is used in the trim operation - if (indexFromStopId != null && indexFromStopId > 0){ - trimFrom = true; - } - else if (indexToStopId > 0 && indexToStopId < allStopTimes){ - trimTo = true; - } - // operation object to used it int trim trip strategy - PropertyPathExpression expression = new PropertyPathExpression("id"); - operation.setMatch(new TypedEntityMatch(Trip.class, - new PropertyValueEntityMatch(expression, - new SimpleValueMatcher(entry.getKey())))); - - if (Boolean.TRUE.equals(trimFrom)) - operation.setFromStopId(fromStopId); - if (Boolean.TRUE.equals(trimTo)) - operation.setToStopId(toStopId); - - // add operation + execute strategy - strategy.addOperation(operation); - strategy.run(context, gtfsMutableRelationalDao); - } - } - - /* - * updateDict : function used to add/update dictionary - */ - private static void updateDict(Map> dictionary, Object key, List values) { - // If the key already exists, append the values to its existing list - if (dictionary.containsKey(key)) { - List existingValues = dictionary.get(key); - existingValues.addAll(values); - } - // If the key is new, add it to the dictionary with the list of values - else { - dictionary.put(key, values); - } - } - - /* - * create polygone/multiPolygone from polygone variable in json file - * return Geometry variable - * return null if an exception is encountered when parsing the wkt string - */ - private Geometry buildPolygone(String wktPolygone) { - WKTReader reader = new WKTReader(); - try{ - return reader.read(wktPolygone); - } catch (ParseException e){ - String message = String.format("Error parsing WKT string : %s", e.getMessage()); - _log.error(message); - return null; - } - - } - /* - * insidePolygone returns boolean variable - * true: if polygone contains point - * false if point is outside polygone - */ - private boolean insidePolygon(Geometry geometry, double lon, double lat) { - GeometryFactory geometryFactory = new GeometryFactory(); - Point point = geometryFactory.createPoint(new Coordinate(lon, lat)); - return geometry.contains(point); - } - -} diff --git a/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygon.java b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygon.java new file mode 100644 index 000000000..98ba9e6b0 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/main/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygon.java @@ -0,0 +1,138 @@ +package org.onebusaway.gtfs_transformer.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.geom.*; + +import org.onebusaway.collections.beans.PropertyPathExpression; +import org.onebusaway.csv_entities.schema.annotations.CsvField; +import org.onebusaway.gtfs.model.Stop; +import org.onebusaway.gtfs.model.StopTime; +import org.onebusaway.gtfs.model.Trip; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs_transformer.services.GtfsTransformStrategy; +import org.onebusaway.gtfs_transformer.services.TransformContext; +import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy; +import org.onebusaway.gtfs_transformer.updates.TrimTripTransformStrategy.TrimOperation; +import org.onebusaway.gtfs_transformer.match.PropertyValueEntityMatch; +import org.onebusaway.gtfs_transformer.match.SimpleValueMatcher; +import org.onebusaway.gtfs_transformer.match.TypedEntityMatch; + +public class TrimTripFromPolygon implements GtfsTransformStrategy { + private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(); + private final WKTReader wktReader = new WKTReader(GEOMETRY_FACTORY); + + private final TrimTripTransformStrategy strategy = new TrimTripTransformStrategy(); + private final TransformContext context = new TransformContext(); + + @CsvField(optional = false) + private String polygon; + + @CsvField(ignore = true) + private Geometry polygonGeometry; + + public void setPolygon(String polygon) { + this.polygon = polygon; + this.polygonGeometry = buildPolygon(polygon); + + if (this.polygonGeometry == null || !this.polygonGeometry.isValid() || this.polygonGeometry.isEmpty()) { + throw new IllegalArgumentException("The provided polygon is invalid or empty."); + } + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public void run(TransformContext transformContext, GtfsMutableRelationalDao gtfsMutableRelationalDao) { + + for (Trip trip : gtfsMutableRelationalDao.getAllTrips()){ + // retrieve the list of StopTimes for the current trip + List stopTimes = gtfsMutableRelationalDao.getStopTimesForTrip(trip); + List stopTimesInPolygon = new ArrayList<>(); + + for (StopTime stopTime : stopTimes) { + Stop stop = gtfsMutableRelationalDao.getStopForId(stopTime.getStop().getId()); + + // add StopTime to the list if its Stop is within the polygon boundaries + if (insidePolygon(polygonGeometry, stop.getLon(), stop.getLat())) { + stopTimesInPolygon.add(stopTime); + } + } + + // if some stops are inside the polygon but not all, apply the TrimTrip operation + if (!stopTimesInPolygon.isEmpty() && stopTimesInPolygon.size() < stopTimes.size()) { + applyTrimOperation(gtfsMutableRelationalDao,trip, stopTimesInPolygon); + } + } + // execute the TrimTrip transformation strategy + strategy.run(context, gtfsMutableRelationalDao); + } + + private void applyTrimOperation(GtfsMutableRelationalDao gtfs, Trip trip, List stopTimes) { + // initialize a new TrimOperation object to define parameters + TrimOperation operation = new TrimOperation(); + + StopTime firstStopTime = stopTimes.get(0); + StopTime lastStopTime = stopTimes.get(stopTimes.size() - 1); + + // set 'ToStopId' of the trim operation if the first StopTime is not the first stopTime in the trip + if (firstStopTime.getStopSequence() > 0) { + StopTime previousStop = gtfs.getStopTimesForTrip(trip).get(firstStopTime.getStopSequence() - 1); + operation.setToStopId(previousStop.getStop().getId().getId()); + } + + // set 'FromStopId' of the trim operation if the last StopTime is not the last stopTime in the trip + if (lastStopTime.getStopSequence() < (gtfs.getStopTimesForTrip(trip).size()-1)) { + StopTime nextStop = gtfs.getStopTimesForTrip(trip).get(lastStopTime.getStopSequence() + 1); + operation.setFromStopId(nextStop.getStop().getId().getId()); + } + + // define the matching criteria + operation.setMatch(new TypedEntityMatch( + Trip.class, + new PropertyValueEntityMatch( + new PropertyPathExpression("id"), + new SimpleValueMatcher(trip.getId()) + ) + )); + + // add the TrimOperation to the strategy for later execution + strategy.addOperation(operation); + } + + /* + * Creates a Geometry object (polygon or multi-polygon) from the provided WKT string. + * + * @param polygonWKT The WKT representation of the polygon. + * @return The Geometry object. + * @throws IllegalArgumentException if the WKT string is invalid or cannot be parsed. + */ + private Geometry buildPolygon(String polygonWKT) { + try{ + return wktReader.read(polygonWKT); + } catch (ParseException e){ + throw new IllegalArgumentException( + String.format("Error parsing WKT string: %s", e.getMessage()), e + ); + } + } + /* + * insidePolygon Checks whether a given point (specified by its longitude and latitude) is inside a given polygon or multipolygon. + * + * @param geometry The Geometry object representing the polygon or multipolygon. + * @param lon the longitude of the point to check. + * @param lat the latitude of the point to check. + * @return true if the point is within the boundaries of the geometry; false otherwise. + */ + private boolean insidePolygon(Geometry geometry, double lon, double lat) { + Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)); + return geometry.contains(point); + } + +} \ No newline at end of file diff --git a/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygonTest.java b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygonTest.java new file mode 100644 index 000000000..586ad7f39 --- /dev/null +++ b/onebusaway-gtfs-transformer/src/test/java/org/onebusaway/gtfs_transformer/impl/TrimTripFromPolygonTest.java @@ -0,0 +1,81 @@ +package org.onebusaway.gtfs_transformer.impl; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onebusaway.gtfs.services.GtfsMutableRelationalDao; +import org.onebusaway.gtfs.services.MockGtfs; +import org.onebusaway.gtfs_transformer.services.TransformContext; + +public class TrimTripFromPolygonTest { + + private TrimTripFromPolygon trimTripFromPolygon = new TrimTripFromPolygon(); + private RetainUpFromPolygon retainUpFromPolygon = new RetainUpFromPolygon(); + private TransformContext _context = new TransformContext(); + private MockGtfs _gtfs; + + @BeforeEach + public void setup() throws IOException{ + + _gtfs = MockGtfs.create(); + // Insert mock data into the GTFS for testing: + // 1 agency + _gtfs.putAgencies(1); + // 4 routes + _gtfs.putRoutes(4); + // 4 trips + _gtfs.putTrips(4, "r$0","sid$0"); + // 8 stops + _gtfs.putStops(10); + // 13 stop times + _gtfs.putLines("stop_times.txt", + "trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,shape_dist_traveled", + // Trip t0: sequence of stops s0,s1,s2,s3 + "t0,08:00:00,08:25:00,s0,0,,,,", + "t0,08:30:00,08:55:00,s1,1,,,,", + "t0,09:00:00,09:55:00,s2,2,,,,", + "t0,10:00:00,10:30:00,s3,3,,,,", + // Trip t1: reverse sequence of stops s3,s2,s1,s0 + "t1,08:00:00,08:25:00,s3,0,,,,", + "t1,08:30:00,08:55:00,s2,1,,,,", + "t1,09:00:00,09:55:00,s1,2,,,,", + "t1,10:00:00,10:00:00,s0,3,,,,", + // Trip t2: sequence of stops s2,s3,s4,s5 + "t2,09:00:00,09:55:00,s2,0,,,,", + "t2,10:00:00,10:55:00,s3,1,,,,", + "t2,11:00:00,11:25:00,s4,2,,,,", + "t2,11:30:00,11:55:00,s5,3,,,,", + // Trip t3: Additional stops + "t3,12:00:00,12:25:00,s6,0,,,,", + "t3,12:30:00,12:55:00,s7,1,,,,"); + } + + @Test + public void testTrimTripFromPolygon() throws IOException { + GtfsMutableRelationalDao dao = _gtfs.read(); + + // Define a polygon in WKT (Well-Known Text) format + // This polygon is designed to include only the first 4 stops (S0 to S4) + String polygonWKT = "POLYGON ((-122.308 47.653, -122.308 47.666, -122.307 47.666, -122.307 47.665, -122.307 47.661, -122.307 47.657, -122.307 47.653, -122.308 47.653))"; + trimTripFromPolygon.setPolygon(polygonWKT); + retainUpFromPolygon.setPolygon(polygonWKT); + + // Execute the retainUpFromPolygon strategy based on the polygon + retainUpFromPolygon.run(_context, dao); + // Execute the trimTripFromPolygon strategy based on the polygon + trimTripFromPolygon.run(_context, dao); + + // Verify that the number of routes is reduced to 3 + assertEquals(3,dao.getAllRoutes().size()); + + // Verify that the number of trips is reduced to 3 + assertEquals(3,dao.getAllTrips().size()); + + // Verify that the number of stops is reduced to 6 + assertEquals(6,dao.getAllStops().size()); + + // Verify that the number of stop times is reduced to 10 + assertEquals(10,dao.getAllStopTimes().size()); + } +} \ No newline at end of file From b6bfec48a8b76860aa6d9082fe68992e68b7d522 Mon Sep 17 00:00:00 2001 From: sbonnet Date: Wed, 18 Dec 2024 15:15:00 +0100 Subject: [PATCH 222/234] Update doc --- ...nebusaway-gtfs-transformer-cli-sample1.png | Bin 0 -> 854688 bytes docs/onebusaway-gtfs-transformer-cli.md | 112 +++++++++--------- 2 files changed, 58 insertions(+), 54 deletions(-) create mode 100644 docs/onebusaway-gtfs-transformer-cli-sample1.png diff --git a/docs/onebusaway-gtfs-transformer-cli-sample1.png b/docs/onebusaway-gtfs-transformer-cli-sample1.png new file mode 100644 index 0000000000000000000000000000000000000000..eec7807587f63d040a864b15829fd0132465607c GIT binary patch literal 854688 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
  • opYW$K7Ctfx0@k6N5JofSy6kE2K>^s1fy=IX!}Skz+9F)6 z_(vjSF5-l#HElW$*qqVV>O8+^VzeEGT>Yee_oNHSi|NI;Cb}k?wmW0T3VoZ3CnX(5 zdqZpV8JC&mPyJg!sJH8C zv#tF2^|J5gM04a$KhPMe6WenSW>c(TGnkh5+lN33Sr~55D}dV$r}H+*?(vU4zEE*7 z^mjL6#APnxvxJTvC{RW@DJzfv9|+zt{K*JQz%f#t)*&y2Q|N0^T>ZoX22zeh&Au!D zc%58tr^K5E>`HBEd8dd~UY&MaF>SkE$^31T<1HkNKn5L>O8?Roj7dW{XUcx-9HNWp z^MFx!O7`Zq>vOE&PNqflUXtZvwrI%Kt2&m{JFDOMlCt>#vR{F{tx{G_-#F;}?`Xo6 zYzep28um0p7zGVj85y5^i#ibg%j3r}MVFS!eo2fWkIt2jg$90f&WFPyKcEB0o~FvT z`0ck&D4Q1Rn&grYHF$nJ*Sl$do{qPt&*2)Pmw-=4Wui!ivkj3W(9zHh1+{T4kJr15 z_D?kPNc#zVW%+KtaeE0HEgf?At;d6(B*wnJ-(IBa&B_$THGTJrd(?la*MA-am!Tf5 zs~P2M9>^Y7CM<35KD8WxLzsZzmw3qs?lHm>s{d{e*d8?cCB$xxzi`cg(-lhg#4ve>j1s}eSelX9qE^xNxivq3K@H481 zU9(zU{Pq6(-HmA>L&>GX-tD-<6APnO-&V|Kmu1&;YIb+U*;k4ao)k^D=x&TFJW2=q zMpzGFVG;0tQ~b6S!}i{Bwt8$yK1zbo^KwW@gt@)?iN@uAqQC0k4qJ z`cP0aVKpy2h3Q=zT!wQMd#zbfzSd%+IWjNLK5=ebt%7c>%=w2l;HJkJIiGGrdp9x6 z!5=3PKvd0u;iNpDBO1jR&__;$6eVt%Sv-_Af~V4CTb7&T3j&{}a~OY^M?N2Ie{~7Z zcp1gxcU;Ib6{zG1p%LTz2xJPoRcgcXtR{(Hhe~UvP4-X1*qsxKzNS=jhB7je$Re8 z;k082+3Y}z<+IuPH;tc=%NF{6YA6LEYq5j1F4<8BqQbmqWp(o#0+o#rnp{PakoYw?TqFz z`_D3%S3~h33uT?(^23yf$HH-n`3UM zHXYWdv58Sw!b6F_oZhS25}PV7F_Ff;lJN%7=IPI}KVPK|7>Qap8rt&~M-E=>7+hTb zsf=zV-t&?!1=-YyBDh*Rdkmep3Jc4cKVJPvHiU!jrN83M#ovcBkP-?W}8K ztqrx-7N%dqL|NEE*4SD7eldSAE2B~rNYd(>ug_F-Izwd-;NW>>gMNBh@~%*~|E%+S z_%o}p>c%}O)Q?bDXWQ;Y1C|&H&=M+ODYC%C0Ws6h!Gn zwJK=?A_irN!^6Va(MTEYAfLryp*siL#HP)6!b^Q3_i?&muNM#jbym!=7D}?3CKq9f z7ZvJf%~2!fska^_yv^;x-^Yp1SqsQs3$@KYbs?d;R@9(r@1>b<^j5qmB?uZyXEZQj(ziV{Cl6W*W!-T+h#U z0)vsO+5P{=#^76niHzw}_AbCfBfFrlfEFkxMam4~?l0xtwSE}ce;ac3zv3#+f3$qa zmTm`Qca+nTC=A)V$R4Aw{+e6fgfG?3Ya`HtMSoXdEr5+kAcBaPWn?u4O~JGOv#rSO zbc4Hqd%5wh;Kvqk7rbK~w77R&AaWw%N!}O6R>&S~4)(_!<>TM}>o-wW6))68V;5z=`ZLMl#1KwWKucg*3c~jwC{Wzg6BuE*;~xN zJ}me?C#u>R9M6qeY%dJ^U?snai~M~#{jQ*YJX#d6Y`H4v{o7<4i>xbA zx)^G|a-HK8a-mBvlX-2VqRvXB?cSH_0Zv^k-P{@U7tu_WFs+jh05EViq}JTTq_QxZ zJlDpoqBv0fxPUUe{+^~Z$=!Jm`@{((UWWSYCN+)UYF^O+%_G*Ez==>H+f6e7%_i!^ z%;Y;aIQU$yJS6pi5_RYpC&l7Ixl}8Bg@-Peo>i=;<`&3-?&-$HCDD8#8w;7R^)xxh zqo$_mn!?RsGU>C`Z0psv09Bi{jAuj_QU!ZT&N&|OrXB)h(8C{h=ktbdjQ3C0-6~Xn z_wjX^mXgok6bm1@chUj+{*gV<34O51uJ{qt&P(uPms*b@=DNEciPyWs0fIkk^|@=R z_ljT18&Mi(1mlu}ZjYz(Hsm^no&;^fH&_GK@&SG`*}Z;?ER zbX4Y`;>VK4saj?M!{xtvgdQn_v!7pKXb#2V5vz11aVaR4mB)m`nXUc0BHVKxRwi%g z1_9SrsZjQ4*W(ok;*l~=2TevIa}b9IVW*8z*~C$WZ`zpflRKoO54`W2cZsk${j_G= zUx?ZQ9>CzMFb+vFhv4$iR*)Z)RU?t6T_;H#SFO>~Y8Xlkm*5Q!O)l?u1$K(PtEG(3 z&*Q$C?$%u-ITcuiBjBaqdQJI$n)<$RUjN=}dHNc)7r5Pn|Bi5O@3Bp ztczd2{6!OC^U_ILglY>VqAsiRq6lH`dWu3)ig8GKJXDGW`s$xQEC_gGF5qP>VQs0ih=)f)Op_6he>9DnpCDmL0{#&Y??Ns>_gjvnL=Awtu9({B zF{SZrVX!U$Nqv$JJ}y%SL~`^G#L|U=-B`W07Uj*-&WxmR*qoXrjm~;bH9Kqay=$s= znHhiIOH9FtGRbP(5EPcENd;F*bSfuQbcaNcxEY(y|1yUWLI+Xc({2aM1|(Ma`uCz! zxLB{l9FO)-2D-FC%lmcihpG)ODJJ$?#fN9aZ!-QIf3Q8^8vw@nGUa12`*>xhc{|pXN-I!UAbIS)1>Py)+D#oVnQb;H8g*nvvxYTzsUA9y~hY#eXmj8 zt#dXM9;|2J?X|?Sj7Fp<55FRBK%ju_ItZHd>0~BZWupJm+1?SgUn*M|zaJ=IQ>cNh zaxKsOR?kPm>gjioXMR3J42U$vo~}2A7O7FgIL^vG_Gq3v52oTDZU6_a8=6r7H;kZK zBBYrr^BV)USgq#LGZ*k0Rn${lkS`G#{w`-I9?p6uSl%MX>{p(K1V25IZrz}csK<0k zV+ri72ULYy22hhJlm&vs>6q3t2;GQw9H&JW5cZfi_++a>GnhD@#0kYgn4fU_{W~Km zlOIr5_%+I>mn@VpHxY0NhmAUzQ%S57*chIulAJuV3MWEf4_z@{AIJdtTRbvesX^G; zvTB)yxz%p3E5z^-DdNcrh^~sL@quz={zg)$XHs~MU@(`beYe>=5Sa7FT5CE5!zE+J z(o}~Gm9oTVK4U6UL(`uW;7J^8GS5_9&k;yZZYNk01hD<2!%7 ziL2+|v~zg|y?4*gIVIRtbFxJ|W-0)M{3Bw6%<)__b=>@fP3mM29`EW-!rvLepiP$* z9{<#&7TCQnG&6tq+oUNvzu$o(x^xIYXW8k;U;!^kf~k*MhV!jSY$Q;JNWLpXq?P9+2`f6XN}?!d`h^SJ+%V~ zn~FZuS9fbUy9$w;y#EcDP(#!|_uOc5nqNEr(_Bg#j_J_<7E!MvGTpzJH{q(o9zOu` z0#ns2zuO_A2yR$uiAJj92oT2>M20e7C*Sv)Z+ zDBa2i_-lM}OzEE6hc&iowjXJf z7)x??_s8D&a&_lr+|&|-6#B4gyxiStK8;uZ{qL9uHOgde#$T?7cZW^0-Hv4kPM>?< zlviAn3!O$&7Q;7^C6Nz9m!o}R(G6pxi2R;loN5X{3qjF6|2c&8bJSpae*<|XtB|Va z;ewUP56Q!_vfG7H*x?Gxynj;$>eFk(u``vBkmB0!_>1(eS0Vam?MZ+5bMWA6MR8+2 zq#x>p9FE)%98R{<$lNpnauR>W9PKvPYW5ZL4J{6@G4Q`+p&4o|O%cO8R?T z|4gKsSbA>%8A=~0&;*|w&h&ki3*HD{gzxGQgPUi638~DxPDzS)TUti@i6&CA-W%$l zbmVxNITYiqr!%4qJ68X@>{Pv8cfj-AMo1a_ewaew-~{P=>u){K%0PH-IVrfV2n@Z4 z29as0vpiuzXC&0jdEn?o^5sF2D&|2 z;+i%F1dY_>3X$&~J5#JppElDv0b7A&j;1Pz@TbP*D>tf4f5!$0J5wn#x`WU@pG0%g1nGt%nZ6=x7Ah#K$qi%lDm;vB!nvzga7L7odle^0Gxo?- z&(zeA7}RKt9Y3|C+G6<@PiH|yf|8p#AZ7#|CX>((j7WPCAJCwpQSsZ`HL~fbw}s^&`O1EU1>Kd4}d8A`D1*1lJG%4T3TV zkr#cLKU(6?nh_^ z{IUOh#f6zF`{U}$$503@@_dIoz9#ArFWxj`4;22an1YaSw%jAO^$cd2t-Shy8s_8d zZ2tUn+y$%pGGr8`9UdtP(EKQGHLtrFuw1UDuMUYkWx?EF@5-?uEUT(~20bF4;Jt92 z9_)Gq5kTC8McL3fnTJg@kC?z~eF$#~9$QU@m!O0YRrGyR%xqOFp?w&RBYngPGQ28jKJtNHl|Yw$~`0g;+%_ z7DSe0eNLItv=iE+0xK_(9dM3#8E5S!>TS%wiN)1+uDj;s=F<@TAVABiBgF1R#eMLX zb}YbCde}KsxX1Zvaiu3e)A!|5|5Il&3e=};9C8bBgF?Fe-3Y}i=PmO)P!i)Qmj0j; z6^e|5He9q8)VA`cKH0r%voGhMkf(3TV6Q~RP9B38`yRBC9J(+OOF%fjhU_BKAtS+uC*tIZ^b`URy3U$Hw zJFXp}GV5DvG=L)Xxz^U>uvA5kzx}+l~UY0I#{ADFc-Wp^Ss~v ziHSDTH(j4!x~y<%%#V4u&W9;TED+&~Fyd5o^c1u}e-#&r;eCD&<8vsIjjT4mWjsBh z8@0Dw(e-%ozFpk!AJ=RWZ2Q=8Qk88&_6JH_{uc{juX-2#6`dgA9v3kIXYXHM`?zif zk6xR!-Wqlc5gK+=Tf2A;-m#AF)=rStLHkrbo%S#D$N#BQJ3@Fm{@RG?GDL;xnHGFf zc4ML7v_D7v_7)V$_Q>yE>4h5ok3_ef`(gLv%ze2`@qcY;cDNZ9C~vO$!AjsrJsX3E zsxD~p^w^w_9#$<>pnJtHEq-Fc6dik!U2c1~%M#0^_Sq^ic-AH733Vv^_*mpv;C-qb z>iaDI_2I=OHN7x89BHaV$UQw-3>gl*JA>ty>SyfjTfLHNPuz8!G;z^6u?hmTouR6Z z2mPIE83$U;BZq*73pQJo5z^S_X3qZO0GCJ}3zpg=}50QCwJ}3J`C1e~;eyKo1ZERQ{^v3gMG{>0FZ$0R3gu%P_p) z=Z_8V>^-I&xRiu209&zPUy7k`Ps+dm#p%tGl%IR^Q~2k~gD2czoxrG764+Vmo#!%j`a%(A}m z_N|THbYnMTn_#~f%kvAHq?;iIv6;+wE`c&T)Coy|^TJvKYb82~CU&48f^FtRVX%Ho{59b{{%@zu)rGl@lVIx6Nv2b8rgF?WfZNn(zBZA#tZnsr> zZl)XSnmHG@U)M{vZ-rm^3CFizRo~~>lgR>{+&+1}MG5(sHv*}7b3Vv#`|qb5EL#0Y zOTF*WkB1_*@Y7D zxF~Ly$Y;3ra%Cg{QcpM$^c0(u3wbAn7TIcF)Mp3dMilGoOaY>RgS2rO0c(w7{0$L# zM*qrFr&>>oX+}&%Y{cgH04MujNB6kTbBEz{Pu?{ORl zG_f4dua}FZdwllLhk3=&JjvAmtAy$)Y2Allb43R9cz)tvdwGwNHi|Sy_379fEUWrF z6&*+)4>o}u!o8~-6iL(mF~ZXWxw=Vn_qd-T;4f16p7Hycp0`wfCgInbkC)|*5R$(O z<~_LuhxmkL1RmNykO$`1{R>j*Pvx#tt{+u?r8-SVWT1zLQWaQB|`S41;97{!a+ASdh|Y?nd+&3ZY@1XVdi zHCEeO^=-S(@*?1;5!{#d=b&CCeYH21$R_&JSMzIj%RJ$$-B*}@tLOE*wV*+y68WDm z!{m8P0EhqQ^7}Nu@gjFyJ?qAm8=cv)JV%wH%a+LQw9oi@&<9H*G+h`Xk>^Ro)@#xA zuWzbaQ94ohR9j|-;%-x6h5{Vxge_X8$qP372De)Fy_4AGluCsGn!(Y~r&4vEUgS0p z*vFkTfmn>QG3sQKTeUg$+o7;%8vE_#NWncR`RS_cd&HvXQWtssR1(H9!UZ!_v&hHJShpll`)t$+JW{-%dF)&Aw5hXBzW3>DOw$oh*ZJnfp1 zOHrRMu1Uu~u1;?##~v=ne_8X}Nz|5aw+c&{dqcAK$QE=}b!;q`ek6`0{Lc-ZtkSz{ zvKZb8Z`)8MlL89MXq0rG8TX{sfx&?jq>5#dUMUCN-28m)zI!{qAuDm~)_S9{=MB=# ze#F(|B7ZUE+dr0w^HhhfOflMVQ(O}Dmdvc)i{7c&ymN6Y;3He%NDBE^6c}%KyMW?y z@h8>29rT716}GozHccCF-D7YzU`P+&Fx(g7d@Mikca;~=1C$HbZQ+-~*=eDLokR^m z5RfBEYqGbNvuiuWtWJwFU zInYbovTP|B5%xe!I;~AmyZ9=4_iDD>KK z;a+gpN+P)Dy2M>EBPT7~R5%`pMl$4o1$8MsxGb0a&3q8IDTJ{Z48wQ+{ipS*S@?B%N}jajw9-we__- zFNONG-xx+feltQUOkzLIx=xfVV9}XwTH#Gh;Jq$lpn{-U?UoRyw1vC3rlYqM_n{W8 zmS-b-`{S|T&#$(1QqVu;X7TTc?w~Rep4{@Low4`%!Y|JRs~*2va)!)nKF*!P;C=JL za92lI-bLE7JML#t2f_$MlRX`92s&SMxw~N7&tPWjs(QB|SpF}Mb?tR^;~VU7%qjRj z5ggMs@N_Omzr2BbWU1j|r6{5^oqQ?!)PG@DpAgJD?I(~K%5mRmmy>{yvb4Lqnw>hg zbv3y1&{G2NSLm&O zNJE+RP@C8BvVAeZYr~d5Zd6aEY=jP9R(s5hxqidHGls;IKH^f9;Myrft&!qU(d3Um zJa2b2e~Mi3#DPl_SJ#AwHvqJ#U3ML!O~cY2}i;Y~Hnd zIZBWkNd1)9xk{>rZC|1NszoV?6siO8@}0c@;cDuUJU8I=p3pS59^C)4IuS&R7*(L> z#owo>AZ3O89CyYg#W2iBvs5N?EyLSXK!;jTNeZ2o(U!7b8Kj{UE`|^_Qw4D}-~k4m z!8vY<5csVNdnyR0%s;n{=p~u6*#{F6OVBZcNSnZn#wt^v|K1V-jyQgh;t-*zf%2<% zT(^QqRaqqg6O?|2IJNIq<~E@3>Vw(~6@7hnHX(*3Y1K==syE=W%Vrwcw}Mev9z4uP zA5BpuWwgojc8MJY!bC0aEFf`CzDPcpRZO_0V9qL69pNKGrkQR(x`?|@3YWe`rs2-(&E%&6C;qUVk^&o4MEv zeb4KCT>`J~)M-RwVc6{lK5hf&X_O|bA;w3(UX!i&liN3)K%gZl)nD*Zsx2i?5}0!l zxAmG`State(=DNzl6-LNryZO`q_j3Mm|gv>Nj1!|_8Ja!n`#mmU0zp*Wy&MFkMAb8 zkGAgybU}We>9lrE!q~2B8#`g$WbQO}I7d2Y=EVFi_uW#D=LbB|a}ac9#M$q`;&{hA zGWDeq^psn;Z@fW-bToY2GL!(Ak}|D?cD*^LzcC38h1qTD&_tBvlS<1Xh$PCgG{uji z?$ZRo2Wpd74H@G8_w~F?nbu35^B*hi{_=K!#7Pa^pEiC6kITr{$y3ZLWA9shgS zFG{|0ij_mAnr-vMYawb z`STAgr}ISj^aL3I+Pme&x$dXe3} z@{oVC?YlWHx_f7P-q^mE8(xMK9Eiw+6fCZqvW8D-yKI@})I@o*U>>csLS3PKzcH4L zJ!a7E_q@mH{#v%ngqfoTnT?bF{LU!F#wWPR9=E)?qH_FSoe=Z;Fn+PV6?&8+zm&mO74-DQ5A*7Y zft~R`gWt;(Ihrxa=kIR=4V50$75?|$ga77;eXD;K@ZT6X^IuyaCi!1)D$4hs#$+kq z{~e$2+v^p5ALLJ+*x~q?@ZEvs`w6NnkMzt`@ZWz1Dri=@YyX9KB;JoLYwgKZ(ftxr zullL;gW>M>U>iWrZh&Z^{k6^*caB2BDCtiqj@VA{F85T}u3VJ0%v6%}Z_;DV|3dTM zL;wC~a{l$_R=>-?!SC`9mhYK=qxvuZM$JFIFZJ*Bze^NlFi84S1eO>XoX$MF<^_HA z<5MAOp;F^4yUIcvS^?a%wAtMBEbA@ENu_$RReh-V-b0F=Ogx$?sw^8mJ08F0LgmK& zf%mgW)LjYC@fZDY3u6-XdL!8&qB7~~y6ag9nSKVO!LO9XHE^h)?4;d+N`oRn2!cbhX z_mF5a0HwW4Uxcj#4j%U460)mhNR^J)3@fCG3Rwr5mp7Z1d{*<*-QakE->L3kslaQZ zriM8Hsa@|k71ul*$QJS)6Rph0YXI?50oEnvcb0mu-Y@a%{&6!IF)kiK?{%qp?+xtc z*R9^T(?WLEU;SPPnUiigqQqy@)5WK7El(U|53Ia>1>H#{)x4!@*8+6LRy}rb`X+2; z(-`o$-A^#if*nuS)~E)(nk1=n z@9PQ*IB-Ky8a7)z^`|DGg3w645W%zU->$Yv)o(;7n#!+eQ<0qObsQXZ(ed9-FXsMh z?>FCz!IpKpL(Z%EuM8?HO?eCQC2)l47jOw=+syN4JHP8|^*7Jh_g;J_7YnJ5tbMx| zXZwEa^-ON2H(~>L(?&yyWFq_UUnCRbclG8Mw&vFis-@#}tade0dQ+k~sjBeC>P?|W z(JTqA3;J7Vhe@XoE}k0x zU{=u;S3w%#jq`14iQ0DgY>FE}b1g#DmP|4S?`{B|_~xf9(AJ#!kUJ`etoYNuTCOM7 zj#%T43>3AdVY1RPTl_kV!MC~R5SPh;cV24yM7bYP`RDEXX>Ha&=LjkY{IkJeSXQro z=fBS~==h#C$yLMk%_NJuE@8G7j5TFp%HpW~cD)#y^?kSw9uA++eus}XTGJlEdp{$u z%14@%`ELA*rBQEtA@D=-{oGF1e;rb{qm8q(Nmvo}LGNVB$LoJxqM7IW-ZrN&x3od? zab#qG1+^8sj0~muGR1Am52G=9L!_o8_#U}D92~9>xCDrgh1H) z&!^F7_ocBN&rjj>?%#uByZ;WZc;o2Xn#rs9@H$8Rz&;ILw(9->m98Ldo<|^J_?}qC&5%Dj9Pk+ZoNKwzm zK!mUL4`3qtTX{Ov*sFZ|Z+;*;bk;ra0(N@S&zz50fgpi^kuJfiDV2I@qDnRr@p$BH zxa>IEjLFiZFRf3wBb9UZ!~DOt~TQgxTK&u zr4f0cEsPXs1mz(q2<=6$1W&^QP`{m-z>T0(0MfwY8?!!Ly5>-Ay{pH0d8aZQ75s6` z3{(IKW7ZIrfLzZ|M{qmq&hFOfOq*30RYFRh?uH7D+~3g#(6n{eu6UtCQ!ZvQO}J&k zpS{}B5%rv*P`6Yr*%O(L$f#SP1iEB;_nYT%0nP~Hn7nlKY{4!SYL@`)>Vdpq+Dnlu zbayNS6UowAgKC184UGj&)I$hEl2Tobe`M8flvm6{9^3kWkpwW-Fd`ra-9K!i3hhCz zI#zPp4WlSj5ix$on(a4gG_E~i;cKa36Hp6H0aT2H+}fz8_kR1I&PDlG@j0oft}qK# z4V|0MUjiwMjSRGzz|H#)P*DDwrsQh*;F!pZp1^RS~1bKVZ@H=9ur%D;343}APe#X z(ZhPMP#W?IC1;CS&Y4Ed1aqJ5nsYnR;)Fm~^ckHk09C*!%`_nYvdr;*3m1Ev+j>f}<{G0@qgx>smn0 zaMM*>-Ntt9*^mYot67~&MG4_bXW2CCK)`rr@dn(GgV`h;v!Q>@ZVf?O0tLwb(qN(e& zvz_j~IsFlRYWW;}{+uOW!CKc%TOqZ6HRnE@(4Tjuc}bo^>@MUwji>OGZ8K4&v$$LQ zlYrYY;k-{@Uw&HsxZ(L4shnN1F}_Ls`hC|{VzQUUvryVJH-Bx#_B=O7P;*PnR|YV= zAL`TXM%;EA)-!DHQDwCw=jAwxyJ2@<7?F*Q3iMu$&%Bf4PA4cpMCYXXqHdVaZNlF~?+H1?;qH^c=K(rtvsePeFkk z!Yo;#ce|Ow%G&Q0xnH;za>$4S>r$SG4=u8=7pGnKNF#pe+4n+{tDIWNy_ab0hdQ1M z%&NF>AcQYMwbxjys+RmifbMeYS=YQCc$tYp=1ioNFgpq@O*!3YWE)h)A_TG!|2t~P z+;qFPW(-Z^LgQ{Twyh>W-Q?8(Mq5M&GBo@J4KU{|q<-MJ19d>Dg}_IslwiY%=Kk>ne3q%W-j4;&tmD~w>KWRO#;kfCsN%_?0<;zV{GD>XQPFJ{8!RFj?T z49TJe-s@Jxpb64on*x_)RfD7gi}`aBSa^D?kIx)RLz#viHFQUWs%8>Ag;Ix9GDBaP zX87n$B4Wg7&p`5XauGc&4?<>yRNmVW@>GOkQWE%e)S-IBz_?P6E*=vC!|$A#clG6z zrv)kw!;DEh9=Z4gW|^YNg~vCM$z@9Xg!K`$4m2Bz5dCyYVGe0VZhI4I9nz5C3S11U z$}Vz^k$@;ffd9>SGYWRed`}6yOW+u79=^S6hw+i7xpdQNM*)6Tflc)cQOr{8p-6xW z2<#Noa#ewgH4y>cC}5I`5r!fC=zPv{1mGPfEl)3=-<95f0OWrHFSFpcPov*)H`O`3 zzU$&;5aD)A1ZRM&0=gbJ1-5_VlW0h)dj1Rvj#JK?+Z{GpylUKD3Jf>I-V@A7LEAO;!Q?a3(+>@U(|=}1+Ct!TDfkyau_f=S7G0UvpO zVKcG~1ja(BlcNE7e_8>__of>(VukkM)g$&im@#PXCT~ zB20cZiM?O-Hwk}RV?)ML2&2p{^kmfZn8|N>Z$N335*^KvE+n^mpX~?TjZOa*9?5da z9}23`1M8fZ{`s#wcer03e^}ToXW0V1p#eC7r9%@y+A&l>7+%43FpzkpShl}Rka!`t zXsM`;e1BgMhk;E}1SmqJiOs{0NXWtgz*9>wx|2gQjWGYR#rT5^DKzMI`RR*79gz3Q ztoQhI3!!#LRt&gx8>h$WK*RpJ=1js|MOUkEs@t10g8anAXrw5^CBqh*7v6kA?IUeq zM^^DliW7-H!tipe$MJTg_qLS%Tio=k2K%e*Dw~=nx~C8$4P%;^5~nbvhGYrTzz19= z;)v}2udz=3Z10=z!O&+~cP{$&vMzJ6^M`bSahSv^+Jc_-zd6t-*eUa@sk4dc8 ze^txDTHhmSrxt-kME@nXt(ZjPB8@kxnMZGg&A(Qrmx=*B?X5W$$(ZfnJcaiS+Y$Wz z%JA>9T~^3(-uLezw(S}Jr=DEd^h<^N;K4Xs8INW;bj&_!#Vso|_{_a)@{hIi9`kg2 zrrG2FYH(lZe)nJSoU-PH2KO|3N*+fjy0`CX?RbI`HUIJdNYSF+huG878EAr=2l$-7 z1uGSeFVq^#$#vTGd$Llk?b^E0^*nX8Ly6a$$K^MxbhNf)qm0;QkW5<6&RcTo-f;7= zHl>r1tz>vPVI?4U@{mKY9OsUU0bLZ3qYLsVb_n{%(X#=C*g-_n_B7V+r#5lTB}n^V?=O!Shx)gq!pJ)0bKQv@RU!&O;jN;fk|@zNMTeFDV65YMj}9Rt^_(E z>n}Cm5Rc%c{<+N~)=Gs6B-1mzK>%@>s$!bC>*F zj{nk75*AX1ax7c{F`|3zisKV`{C<1rD9}ej6SM;loS37-2qMISZ}c)XyE*=tKW;#* z4zfCc39A^YMO$PR&z0(#vVJ0cX8o=?v_+e{m&jDrTJPFqhx_x{^`1F@^*XxuZTQ^A zlac>D`Pjul{xq6L`AU__9$Gkv?gp$JFINO4KZmzaipCB-hm7&}#as2rp!Xg%GAT@s5R;aT;r?EXd_-y4Es7+H>KN#(P zrqw#4a+m2ep2ACKa387haU+f6z6ut%tHR zH4nN*pI*2)?*Xoev2D{Qv6BTDQm%y2s&D>d!A*YL;BIF%7mDF^6$^8wYa43(68>6O zdF0Do2DMwz0ea*OT96FK{^9!;8QsH1fFzH!U&iRn?7t<}zG%}zdx)t_6SZMsQb|5! z8>!Si!dSS9=~wA=?d@ZF6qR&R%txYO{uxokQ1cM;T3(>WyW8T7{{?ULg@QnqQ;mCtXKp`WmHJpJ<6QOSyT~hjWBCs zpo&;Zw-?#JQ@bLe7>aqYDKWuDgnO9Oc5y@40(6zp_u_HKpEeZv&w`W`V{^6pI|ibY z$K@&feN*_HDEqFcInKYI*89A^R39MgyJrO-vqjqsYw7LNa9|>$sl9^+=!69*nO!X9 zrB=a7qKnF^Jg2yJ81J1!*;6ujuO;S-Gg`3uuuUDu7QjD~L1fBKCYI1(Xu~)oP$dr1 z*yRBoKFr0l5S@rS{SSutPKkBv0($`J9R=FPtY-9dtuFBgAeJb~^so~hZ}$)h;DZk` zaPR8Ww|zjFj*;Ow!~SV?)DZ=aV&-4S9n1(xiA;?Ud|HBxG@R%Z8-5%4V= zTU{ItnD_UC`07rhZz6SX=Q&!Nv(RsZSS2zon`+lz&>e;RL*q#dROFnDJs;;iPPhf~Mdy|GM3^4aR(NP$wTEWl zzZ%pTEk)bu`fQ@uK^TQw3_Oqmk&13Rpy!E;k~tOAW9&BKnpF&4;X2zAQ zFpErpq!A}0?fYXuTBM7t;*t)~CBsSe`WbKwigL){3uEd7j?8Of+! zo?jsWEPJaotyADnipa6UYP>8J)UgL3rCUUa zQ96kw+Ya-t|CVl7r6STdVYVNB#3wQ`)o&L=iz*|V#QmCrQ<4JglmJRBF!U3d+!pX? zeac7DQ-bhw;vt5%jLRfAyJ}Vq1oeF~7Khm{nriNf$TT4AZ{hSCBr`^>tL<#(sw}eB z{N4u!X_N%H+=2?jk(}S)*GHr1)wGX(RtfZO+|(h5>GDrhXNxFAKxS10+t7CsR46Y( z;>G#QH*hTBxtI>$)_4y?a?|=N1eWeE_dVL4P9=V$x9w`xUMYj0BEq26k2#T>p8T0d zUxBYr)HS2n*TIwBe}SdYyR zALh5&B0z0;m|bNll{Tj>h(ZSEDBn>n zGaQal7uQB4PbSDJ`X0hznnW5dA@s?ZXTCJmnX#lvm!wQ27_WiEF@&yp=&G5Qcw%TN zcWwiY(^ayLo{>ZxAym#+r6vVaJyFTM@jT>~^y>NdI1rU**uc8mc)b;QIdG?3b1g+X zAp8n#1?UVSkY*5y3QKrkkrwPNhz~N-3~B+CLDwQ6=s-@sb}YXyz4vVDwzhpL^CgwR z?`YrWs49C8;#|yDRc}^l3Fk3uRw`FlC8GD(woJ2d`Pph9#QC~XZ`-T{&?KmK+6Zfy zAz+g3f@cw2m^*@a>MbIo8;`#)v}2@W05B#jE(5tN^0xPt$M_q@zN};}y9j1<%DW>D znm}W{O8DXu8~&WTZ+LF#0-QeB)lYbT?BgId^_kbmPi-L}%I_#SSbH2h3hv8fuEgQ! zzUy1J|F9rkDUD%vJZ1DV<`)t{839cm-=A_|p}ySsYc#Jk_+F@Y_$)n?sZ{1mP36a+ct=m&#a>22r|p$2J8q2? z^PQGr*qqwiXf<@?4}9bHiKLR--WC+I)*$Bfjrz#)DewZIFRoo2kqHXE7V04|pnn!m0j+4pE>gx^Da)96pP8WEV zA&E>;&rD-+(4Apsu}KS-ja7qHHa(92Xwy}Ve%`iWIs(^wtYU&D|f-`Y@TyJ&#e=AUcX=XIq!eKjNQAG!9O-@UE9vQo-;!p6(F0e(9@T! z;KQ{0pzojG7HvEs|K)HSMuH-yoJC%kLH<*_EQE>EUzVgmN&Ys2NlEMOJ+tSfPJhoJ zE7Yj4%VW7klUBH{25etc$OvNy8z(m;@EZB}#@{iek5Q#=Q&u!ny0rQrG>Az-?{u<8 zI(_Jx8t9KYb&$!dYd^gTq)4k<1z~=OHHYG^))NFwCRcH%%2kZGy|@`hYXgp@ zTf^zsH#hV~34lgl=Jqzj8Bp5l9WZxndLK|_vCj9kkk7gsmv8Dm+0=aitOTKsoChgZ zX$$l`Te&}Nw%wTWGDuPLUGc*-&lm?IEMFtY?riuwAdamgu_c(Xi~U@sozH~m$hwv4 zyFU^<3#;*w)r7$62cV3PhOQJP6Z3d{P{Yz<6hJ0J3<_mKbp8PgXzB^rr)G{r(q#>F z5=2U)f#(voAu9pbZI9qSxUdAqHncc~G2qHyP7PC`X!g7Ab_ilzgEK%b0_V04>b5L` zu{2^vK~yW0)+FOM3qz)O_ig@ez@v9g^}QU@?l^wR>;e^k%H7Ibz$F*6Pc9| z)7ds{NlRYBK~_*n65e}hj>`@dHxjY9r3IEqGEG=x;hOPPAql)a43fNM<+)%E2Q1Wi9)!PSmKNbW<7%sJ5s9DVVZUe<=~uV zwVh_4?xKnHA-`>|piX0s2xk&e53!mNGZ>S!F9teEVjS#u?S*DO5BXieXxH^%PRQ@| z`e^ld@^agbzq02PoDhoW^JTr7- z&RQg7gBeG@xcP&p0Q^(#(|nZ}4+WrULO%ggiSRovissO+$xK?{EL?*eSR0hvV_IF8 zprPXs*+5l=$TAb0B17pkstr0@3@K*np(rK1QDG}Mt6?;$tcp+<42DsCN6cHoM~((4 z+w$)?NVx4Q@QQ=i4pYe{HqMmJ_8!7GbCcYKaAE8uFG+9;aBN>(A(;VSOry=!R#&ja z#ANV51}#!TzYNGOi7${Uh&~(WHsU2uvyinmqbiqcXH8TdR}$KY@dqU>0O%g!ir?v! z*C#`ivufc)!0*6Bq`DHSTdh=2@WMEbEj9W)qT1f^kSKcZh?}-z;OfBI5h|oA)k~A|w_zUzSkz|3X+D8z!(==(6-8P-VF7!@@H?Pe! zrRVhEc;WMM|Dm1A*Bzf7Bn)6;c5dnnX;x)Y1R@W`8`(ej70vUi|CPG;Z;cd9UVni= zw{#w7_f2k1xePj{?8lp(m!!N_MEsZE-))@C|1VJa=3C;YvB@ZF0P5%|F+@mhV$0L7 z8~$rZ&$%PR+UduvaBSGkXgNlgMggzT28$4Oh81uhjKT#fqISqQ z<}>}quiczz|Ck(}PY4_jfiABG_)D9#++9!PAVKNAQiVwroFEHq3~Evqjgf-Hd5)A) zCpYmFJIIJdxdD}!=Co8ukcy8tkj5qr9uqFF3Mc}a+2L3xsZ(1n6U|SnqCW5ssoOjZ zBm)8k(IctV>(2K<)R5aadTVVF2bUWN^0+s7{M{C=f-PJO%T$}rWMn{=o{CBF-$WeF z-ra!+e#bIHYSC{aeVH0vHM4$z&v`oni!XuCqe2y);f>+eT_ zf{TSgXPd&=%}uddc4EJ=Hka8kKe`CXEr3l+hDWFdNZ-6VdoE*k(uN!Jb8kf}!o}Q!-;3SxPBhiUt|bu)tq|sDsE0pg_Qq z`{YM& z9qdP4D7#yqMm7c!WLAm!u+Ct8?b3FkQ3;v##}D zvs9s`3vz(de~lx1X*my_r0vt*kQFk56f6eb06_%~70$YNck=HvIK|eIVnpT3E7wu) zeoLlKr$4Ju$+*fPWx(JeT^p-tMp!U>SArpkMTkm6pqW%{BgX&Gz9HeVxk@?>+M?|!BNC5+h2^um+$}zR z5B{R?-~#ytmeXj`IYsQ%s;Ve6*+=LJ4%nuEKb*=0^2KP{ zOj1>f4t|>wbX+mY2H}QNfe|2NsbpB!f8y9oLCaKM}hmZrMp;NDo zvRJl#r*#c#uF_|j^1f`HNms@WpguBq)1lqtg)l@+-cJ|azN=Gjp+Pq&nz5mVpJY?U* zKfDlXre138IQ4ksde3GLFy^FWtiU~m^MUB;PP4h?t#(S7053w&-LGQase#3_pN^m( z02R$+9*(VB8o9Y=Jict&#)0~%XC&+6^wSDa=5t9AA@csSniAGkNGYrj7-D83k|Wcp zy3*IdB9nAnO=bD0tGJ{ON4FgC(k}8&yvR=$r)V{amJaXJ(0rDppx%!-av8SrS7v>o zZuPA=kz4nXKJWl(F3(B`!%xl+!n5D|9nr{dyjIObVPmw?(Ic%stSt(2L>h9Vd>t( zfCJ?cnkYJr4#FW)&4e)~(nJ8fZ$?w%TsChao2;&;E<^fs${?qNZB_1R3#2Q!mgn{W zEULw9PAqs8DQhrQF`^;eYxTrb^SjT$i*is#RAOY~)su7*Jd&{eN(#3I03MeJyW$$)9LJ8V1aSw%}Ks(@l|1Nh=UxyK+QhJ43M84>*ZOr;q!$LSFw`M-P zNDnH?e5I?2ZOZ*HT(Zz`%=vCujwpuqR=PXTX*-jJm@g^Ra4s)#&nKB4D=t5dl)m9n zMNtF5ONd1qjkvEzC0i@th(8FH>s|mGMUg%#t&^$>`%4~#o^IpG-t4)qP*dzOYJpY4 zm?08Ot;JOOh%E#X3Nx7)Ys|L`I#}IH60_bBPXd#hPtqO71iH|f(Xw2bM7x&%PihL@{3KUtzbm$P``j0z5H7z*F>1`BUC2M&u% zFZ}78&pi&gDO7c7X5?)9)?c*BT}2`@>lZ?cpKo1rfnc z=7&x80o#kPoIRvqrdy&!t=pkQ)%LD`5B1LuNBF&x_*cIU-msKQQvM8XE9>egFcO8| zH#gC~67FH1-`=mIL#midmL1kBoVS>n z23~=#H`z+3NEvwwg!ybP)$yKvq-E*qaTnbVIU;?l^~PDImHs!*TGJjwyv4Baxy z5J3+U%WMD%TQ)?&;Eq;nq#GPmB z$>iH&k#|mEx9?wT$Z7Cec+^ z0Q@pXFKlM7QKxZmz;-2lq($@rrItNR9Xc|{W>XBA2xtX0E`Tx&E1S5l0)tNxOiCnX z52h=8;9Tx-i>AqiqOKK7ZNx78rA-+NP$WVxSTXZjY#Y@^mdx=XMKwg@0lY{aIVvkw zYD=jC_^LhUw*u})U^A;~RlPPbx2c((m>_M(bv6K@;>8T>wbRE%{>u+!dCT_pKW}o!3%4+C*_Afau`PWU8f>Ly#2cdmxA}ZQRHyPm|2~UDbPz98abzf=#iftuJD&a^;pV zBzdm?-d+_~Pu{^UiSLD5TxGj|w~l(Y_g|0*km)dzys34RI@*lIup>5TB-dcbLCF($iI zFhhhGy4J14asx~WPl7tTX;ZNt$xvx}3X`dKtPuqWw`10l#;3WMf*eyoa6#un`!Pr* zH)J4g<8ZMKB6Z>z6yNI008v;xox17hM!^j|Xfx6r2slBT(&6;NU83C7O|RN>WnXP% z_~z>y{-W<`JV8&YhSjpEFX+;T*E>ujJgmWGQVYw*EIx&3f0`MR0F2pj?yb%SHX_I= zXPRn6IUVmEEpGrG?r03i*kMpOM3@Ai{xL;i%gjHrU&gm4)lTpU$;V8Tm3X$p1cESS zCIX9txkQ`JRhFn=C}jq!t6fCc@TH3i=I@S(t>NC24BT2N8?>@Ju#ISD39l&B5$D4I z$B#&1Sr~=5KL#PdR))7`?Hsh5tljX#OPZ|UK+~{(Lgin`67frhDz6n>s>Q5ZU}=;B zemcM}Mv;K$pG`vLUt4jdr0PpLWAWs#T~ZuXk2-8JvYGD5LKHI#^3ie{;p(i@Qd}?- zqsxQ4zQd%#D~d=&bG6R$ISZJG%|)qbMyg?dNdnfY&Qw|LmoptveG?>h76Q7&Sp<$l z^G~xjYL>t->ouB4g*bHjwvk7J6^Uo?Hh7eMKYuJx(XE`^@D{nK|I_?2-6K$nrVExhNiDRB&PNh>k^7#<^w?w4zykf@j6ZPf zfF(?+<2qr@yCt^<7IO6EM2n!9nDk1xX&dP>fq4WI8N47LzB24xWrQ;oL#6%o_*PhF zEi=W68NvtoiYhM{*IbP=lkXFzG7V>M+D$WxA1jBoL}GhT$15M-AptRS>%;nR-QyQx zeRU!&9l975G2@U{q&>4Xth02o6YgF zm2i%0(cA&oXn_*PG_>3;i&t>$ZCg-jx3O~Nh#_cXiy7%+I{n0HW+CL@dv|{{`4Lqz zU~*~a-e-{PhB0fXu#pKUd_ce>1281?4yWjS=q?;}3>``hw;|n!V*+9HkkDkxXk5rE ziPhQiB?&y!>{B*_AND@)F~L!`@19>AgQV1P6I@7i0<$2ys$3ZAhZ=fLx^mzc;|Cq9 zOt6ENiub29lKPARHi19(n&cQX$3igKs@2PTRvCpss<1UWr87Z2&&-1jZT!QV02X#_$4L5GFY!Yy|h;< zd{?AJ*dMGobxnsaEYmf-*C#P$a+khBJo^*TL?j0+R_mOFg#MoCx(DI|+npifZ*Jx5_melQa79*7%~ zp|tHbg??(IQjw8^SkxJje zlbWnh*)nbYlTNi_$ggbM@y!|X+q(aeW-&Thf%j?SG|!mDVhplxeS6&I#}MfC*?fC- z27eml`AMxsM2AjuLXV!ew*TynNkNjl0}gHNP5hq6!4k9o#~rxWlb57)v)-=%<~fVj zFJ_N4e5c^Qpku4h7ogMq5#j7z=R3ISb>^Pr4F-Pf8mSdc2?+SB@?YZ2gZjudJ256h z$KgyDWi~oCV4eJiz!lv4yg zGmR9=`~|tUugRAj{ZsspK;F-|zwwxxWTx10!l2k;;MY(doviCm#*s7?gcUQu{lXT> zlFmf2n?nv2n(WSk^ zN|0!nDJV$Glwwi|-*>h_XAW5q{!I3OB|N~`XTZacz`}uRTKKvHwoH9<p`L8uv}DXu|rbT;mcgCN{i-x6n&k(9UGXZOm4K1N*GASAOGHR>;W4q3gxN z?8;&xgEGyHQV}@}u8yC47-K!2#h|Kq!S}B8kci?3$GcZ<0m{eoMmQ2gOjNSpVi1q?3|Y4xuAzBQo-aEt?w@1iFHiMr*Z-_%7iy0|9>rh^ zX`Pw`N^;_X+<>eGARN{e(1i<^@e{bwSMyeJ(kwtY-xisvZsw*wP@D6Z6$1;B43Vq_ z&Et6T6*a2vnNoTyq|>DnTvFf{bDETBq|qsGtcuv@2Bv{Pk+e!kL*2zS0e=H&D*}Ff zN@AXN0!ILlhl$NtJm`oNRS30^mRbV5IU8}7(f7h|KAHs5L;h(0u^tnU`-E9O(v(A= zwR7*VzX8cr*kMA{+Mg&>5L1a^vZZQ9QrxGV;)E-gj;Ik3bFnnf0@eUd5u!%)El>ji z8qJ6TM|L(c|1;VNl8)U}KiNZ84ukkhYTC(S`q#Xfk_sTV)#fHO40jz32bh=ke7o}@ zv}QVs)%x8oX)H7iol#y~)me5%1^){#pz5A@DO3cNS4=KMCeNBq#_HewC~R0g^z{aZ z@@$GyuTfpjFVFXxtujd2n^cdXW1%6mA_;d6u)+V?g*b{+r`)r>M$%w|jgsp&-YSjN z!|u!>oi_n$|H=FA{!DSr`QMr8KVh?>oy$tLD$u@@VacO5OOWFKa8hlQsMGfg|1*vx zw{(P4oVW!2cT4Cs&L0Kt$L#d-B`bWr*T-<*YyJar^Zx+sKoh_GaPpp83pfR&29(4Y zu6L499BNc1pLxX+hAur!4YtnFbb$yATp(NG=q#`LTY|k9^IV*X+dg9m8P5f{izVBZ zRD!C2DURs?!mbFTkcyr82D_^UF%#eOG%=L4YR0)wpnm`^<8u#D+sS?5n0O6tT)xoh zu5{{%OidNw3Mu7}yd;RtiYY2WH}3-z*n$s;D;8236?REA_4v^B`KX_7Z0=CFA8^ZS z{L!a{&wo|?qyH&<;cNVT4+($q743h1bKxuZDJRYdX#)NprAeY`GDfsF7-T|)=<~VU zpeRTWzGZ?S8n5lG0R-==W%hkeFhj4Nr;g zYSy?qwGW{Fg`kG;C|Sz{30iyERh=|8)In2{wmhyK^X)L}#aTbeXH>epm?F~H-Y3=5 zIW;K~=&hV3VP1~XnnDvqkY2>4nHHteC`JUr8P=lgZ|bG_Kq3&AwkmeqAn^qlLHB9 z{3gk;htV(qq-dI0Qh`grIT-+gJ}e07M=oNOaX>i63OedAF52YLC{z@2Jc+$iLb?|? zr6;XS3d`sWm1MxQZcH~&;5ED{EfN?5v@G4pg6Jg;d@{;-FJVJSzixY&OQB>HC9MQ5 zo*XTVFUkUF%OnZW{qu}D7gDkDBK!Qd1>cf*1r#;vyQvnogKhMImw9mk0%EIj1>(CK zcm0e$^#6Za&=y%O{GPh9wzJ4I%h=<`xWhMPwF(3nLSmYim40&kJf1oY!x$R+oTki$ z7J{0BoWQ%ZUxOinTwbpO?(z2w5i!ZC%mTVQMX1G6pAyE)nGsew$p~_#tdvlPI@or+ zY8$P9)j6rti#upwRj%nEYL-X9fS$x>XcZw`=>fFp7ReOp^dYDR_Mos@oF;DSg=vIK znUdBGM~0-q2F5ay=?24Y*vIGT+7k{jB-3dR5iUpqqDrU;RC7EWXeC2u^5}duE5bkw zS+Ql)k`21jheH=|3{6^-ry@MhPe~~8u_i|%8|Hb$bCJl6WPT`djwXgB10KyaPmyqL>ay+E3=hXwfZTJ+}8?sa7^s5Mq_q%hIi%uQ(mWgVWqdWvA({w?)ok- zD2lA%K6&7I{Y_%*CajpHq~CL*aLOJ3tGNT%ec|l#Bab{fSoQjYzM(5bOk)z`_EYYl z|9$;(2SmDDNKudshC?O_hnwpN9(_SHRBy^1mos2yy~nSMBoyH_*Zk9eov&}v?_PH(|Mj1jMIJUhO+qnEVD!35 zLIJ(@`@g%h1@#C2U+Iw>hum<#%X-tF=3`A)#?}w zS&-3PJGXqUgyT?zgi|Ar6=xq(sFx$GY1^ttRaT2A5WkAE3ya`kkGA(c-Q&EYM z(`BTD%pYdI_ie?VdY}q}7;YBrM7bz&!VZS)$mP9I8jjf24fdg<{MR2)J3T3kM}`*2 zz#2uNXJF)oDDZ$g+%$qyq!~+C{JwLc=L#&%%Ul=_Pmeu}cLc!;g?cbG?C}qv3}n#` zx1b*I8eZRt+9HFYt(282m$7LUSCkn`E^7=dkO93af|O^pmrUI2T;2J zxg)kv!~SwPyHJFKV)h*oU*;85B}aX{+~+w?QTma3ZB=W~{T3r$^s7RhWGa9$0fJ;)b@&Kl&Sr|C zN@`gv$&f!n3WStdV{L6K^in1jM3sT$$SE{tx}?wPi(DZyDJRB?JB+eCSIS&S{E}=z zy$#{N1+=?`Ud^&RM0BUfhOGOkA7&wa7|LW`;?5{D2R!ddu^ABQlA5r7OwYa+fHodt3oW$Kh z&_F7Vr%D1v;xSSNLI{o{A_xS^E@N7iiUHKZaNvJqQ0v6~HD^oZ=!PSf-fdzMlq|rZ zag;z9tLVSyij$9`f=(OYRthmI>@2JrzrsQ=3TRdewMM_sAYOtiZTjo|r01k=FY0G# z@_-LUsW~I@dJG9+GF5!IpL7udSqL6-b_fiVt6DVBvyrFNl5uG9GKDH#y5Bazzu_nm zEH2Aag_i+kmQ)6&K~8LGmr!+iNnIPM7mE9 z1(BPIfO4!wB=9mAgQW+O4k8yA7H5f<(D|W~V)VmdL>WpQMV=4Q#c4V!g|QC3VJ|3G z4VfOh39bYn>_ib`f}jr|c623PzfkBAj)br-+XaYw#(S^W@YmpL=mdP={Q3pCQiYu_S+%Ix(8LJl z=|&#<5WY!+8QiW#2wv)baG{WZhqI|;;iBC)7q`N zsAT@;>gK7F7oU3a8T_0&b>YbiqhrsWd*=9wQNP`6%}A2G)g5GMBunCyJLo^(W$xHs z@Bi#ie*8Co?YF-8mtXtzr$76t|M}_v_WS?W@Bi-q{d>Rj2Y>LN|L}kOw@>`hAAj=m zpZ{X5Qft||MgM(`aVFC9@jmXn`?ohG4pZ6={e-+af<|J!_doqqoC z6~%AeA;v{QxGcoK>ek0mFM(>1`?-WkJC(v3Ngw|L5#}gY9Wja1d~A-9bPix2490K;fn2K zt6gjlieZ9?kxe@>KM3@z%c?DLJSF`CxH&7Eh7!(da!oCqP7m@OMr&Gole|Dr(P;ol z0%}#Pa;a3ZEDHlP+=BF%G)19{)j4x@WxLbqXHf!AnVW5tOD2Z38+L}t2&_?+>*!U; zLD2qS=wwV@FIQkfo8g+9jAUMh!mBEExK&0{^du*x=n4dRUKF?qU9#7Yy8;WKVv1aJ z^C6@^Kx~-FN4xZ>!mTI|3ib?D5X_;nZhZb;vff>>;aA1p>)$%ag?4+8W|^W)(yqJ^ z76O?83$#zig%>&3O;r&jIeBBJ^aB^#v7}fMgTWYsT_KH60HsT-Sw`}LNY_-lByn6a z26#n9A>FN1{0cbU83 z=@6hG{mJLiK1mQ^NSE*gEvP^K)kSNUxjXunv>34M)}H=VM>_p&V{4N>ApY=&2dd?d zuQ&M!Eii*9$$~Hgl|am*jb0piv9AgWL=^Qj@SeNEl@% zJa;Kpp|RWK=!2a_tcY!f=}-~01!Gr3nSt%WfG1y`s>xCHHh58Koc6>QhnTpKu>v5qM zA1hD#63ivgcc{$Z6F?&w(JeC!D|3d)S@ggo&eTLi4ymfbwL~W8MLIih!p~ zURts=^ZlUL>s`FK{O6zgi@EvPJ8rve${qBRuOse2D`L)1!~h++cB*~jO*eC54oEz%Blnu^4T@3k|A8D5?q!lt zy!y>|+s?W`vrxk@B0qB#P6MpLhPljP{PkSlbzhqSv+puk6@Jc1&^lmKfB^g#IxAr# zuW)lF1%|*oFlybXoum0szq{(_sO-Whx#yAe{5HIW_#vIyX1J}*(Ya?Y~?L?3=f^r2f$zVEh+fBRj@4}PzC+by}JGu>X{jgO0pV)pcT{ww#0 zPaMOb3H4eoiex;G4TF8LK|fbuZy;9$5{9S(E7(E=#ewhPBcsH1q6nCy2|5f3pMiHs zJfKttctS(OOuUh_bks_pXa4FP?|NHRt32`4Q-Au2KfC7IYa7jaZ!p@pu*LIY zvtEa&6$FuCYk%>%{)3Mc$DdV~FUtMR3rVn|&)r;<_OGsQZMU}+Q`0rm^O93%E(C5^ zw;QlGxQ)qW0h1(|tm|uv2utPiLK}tZk{uKrpo!`&n59pAdW;8#i4mPh^+NN2Emy$s z4C53oRf%Jv2($!Nq(R{aq1IZ^+D|ijOOS0E*3cn<3ypOc$GF81I5yj837HhS1(R^D z6PBx1scGhFlu8l9(I9HclokqRT3w+j0fvNCxbT!@tUzn1Gh~k2K zt{rYVpg5KfNSd)sRc!(=M)3$@&XTP0Gb{#n8|6uuGS3bfTmoHzFY^p*N&FJeXy1H1 zJH2||B4_mI$nAGlW)@cAq^cqj((85vQIge5ClX&&5N(1%=t^(-Me;xw6X=uO#y*-UcdU&49L5&GC2<8|<56XFCKHT~wcANYmrDIH`o2(r zu6UC!1t1v+3(V{H!==^|WD?$YQ!g&!mO6*Z^x`h1>2XmcxooPkBtq*y|0aqpm!U+< zG608kqHPQr>UJr`;O=><{MjGR6$LxfV&44jiz%JOq;~T>=wigg)W$^$e2+^75SYm0 zW&mf{1Eo%0W1}H6ILn#y0Bd@p3h`w|0~bemOpS1i6u57YdjK><5^z}hVqDN_Lon#^ zl;hn5wAu931P;BJl4KAMP;HT?d!1;%(eop64zhlX=PxNi5<7{DCU|KGi%oGMl0yjq zHp{BzC()|2orf&ExLg8hj%^0v0gMDu#3lXqLZNe?^b|;%+m^`(ksA`9nibU;=m1s~ zWQa0EEKvDSjMO_h1b*~D42{tcI?{{cbK~B`Rk$SM&z@vjyV%*e945!mlR@rz;J!D$ z;Z}wxB4?xp_%xM)UW6XPfZzm3Wr=o4u0So|^59m4jxen~)hotTe1+0;w_elSLCi%y z8?vd->a%QV4^6tuaYbn#Gjm6v?AdqVsw;PwQcgJg?6GraP7nP!Ni$WIOkIZ-0_I;< z+)=I6FJ4@6hMuNa8ZTcs^=z-Rp}lsvWA)tnPyE==eDY8KEKOgn0KV_u`)<19y4~}; zzY}+yIlcO?e))Gk^Cw?A`S{suu05QI$&@=@E8KyGL_Ii$RGM7~vK55hsQr{XcFN_) z9sEy!>?h!XV<)6ZD9#)o-19Vj{Pcl$70uavn!tz_GEGA9nomN})my*tXD!$d%F<%- z@BX>SE7K$t(*#Dqq}OK>im!Z89u8?$-}=E!pbLq=V-kv8BK7HC}O98wFJ_X$dy2Sv&yCFd~gAF}x z6d~=IS9Z@zvt>q1Q}ZwQZCPfG)G7vt8lM5wt6)&cCqFHK2L^c zhl$ME*5Q53+wRP+J)G*KyWJP#@pSQE#65nD{p^?73oBffv5hJ#%M293*s5Q|7x+~c z6u^;0B@)J0qH`65gMRQ+-=Jqq=x_%rL^%3b6F?0+AuReK5*X?xc<0=i3u&IiJQo%h zjvqU*wbj1m=9_WZ&wloE@U@2?d~|kpW-xI6=+Bw|^asZA)7sgK%E`0blPB2w937k}|tcsMo1W_`rCgM5&1iJvejkWwDx42G4D5>w^W zmM)-sfw2-Jr)i-J^k`MU&L~!TzKZyvHjW=KfZO>tJQ5TS4+BT|dF=Fktt!i+!G&9b zY!Q0a?28JV1#-d`ZKxStQ!Udlb*)^sHAPACs26p^9A?Rk9~|rG*OoTxrlT1|BOE<9 z*K^`pr4n%-ox){P3^w91Cmx~e!i-MgfPiFP1&>#yip|?Kz6uKgpOz%bq-RIxWEQV0 z8gFiob6^@gJEIEwENRA2DrwAp{b;tjyE3#D&;-8 zBvu3hbK?LBFYpfZVn$CtC16zv5jq@-0T=uD0J;?K2$4cUkyW!yi74re1!7sQ!Vd8o zx4|bz$pDO%28rc7F{)qHSL^-MdCA^wXf*oi@eeqzp*G=D5&ltUC%ZYUsK= zFZyv0ooI`tVKR7r`Wgk?K?*ns7Xai@Jx)96=VisRP9_xi|u?PS{3w zp^a|lOyV3WTPc{sVo@*qT%ejawzvytxLeiW}9Jn8zrtjG^PaR1uI zHYBBHR}e2==G>tw@*n-tAOF-({mjbB%Fdt11upo5|KKA_`<73Mrf(S?H5dAiu zmdMI-h0Oyd88PPEcEmO`Nf#)wWo)L8XU|~XFfE2oy(H)xsZbj740sm=;bk6>M5o8E z3LsS3Fxh5}pRY?-EXvpJ?}}1%aii+^Go4{+vsawo6q;4A3|)f8AWFyxi|*wSmSjvm zEQZh#kZ1IWLwH@mE4r|?*(sN7nDm;Tfe$o}E;bYB8p?uj0PH$Ql(0zw+Sck^?B(>F z1)T^ID~g8X-*%Jwy*GUGs$JbQ<2QSH0Zu0D+9vbBW8{;cE57uN;*lrGnRCo`kBMSN zQ|JS!aiL5dWG-NQoScKg4ho_L7z}Ew(U}`s2^;U)w6-c9ZKrU;0C3bwhpY z9sK6j`r6vsnNw$GTdnogjgNfq_crJ1IS0TA69yd;fdTSNl_ZzraySl?TS+b?;yOGf zf*>tqjtDX$37!XyUmOYh%W7SLx#okSk7_HNqH|i#oKV$Pz+d@!6S6`3;%Ohbc08@GUE z>FyN~f|C*>osVV)eZN#PpGSZfm*-T(^MzV1Twe_13|6VA=q`F?L=ismAerzw4D&ce zxLRQBG6Z`ZpihUi7({)%sqh->N?giwRvHUFU~qh0S|GjPr+9(xoka2gd`YO7&PgGU zV!0I1(L+ zD0rdM6LF!|I8!LmX>*Ri>??8&HNgjP3&3xfM}wpfil?DY+W6&PsXTLHXKRD1k-z%` z7ey(9M|hDR#R$yMJv2=!Y7&z$Vtj}ZsCL9u`21KI(>>h~25}LJc_H-A5?+k0#X(FD z`-5bqk2RQ_#b4qcT{*%^?Qk0}oOD!?=us57j-UIR!5WA|7SLVdX0UF`Hk}t6Hy;&% zfQCf@@bQDTbfPwla}W>v$w-qFx)S1&g4&Ma0L`52q0s3Wi8>_5_#{CD9QcQ|Wy={R zx~@pl#5TUgLdL;^n?un;;V8K2b^v-JqsWC}qkkx+5~eDbfP4rVV3_DE^h}V%B;!r4 zCNnxBeDn|MymQ5I;Si->IL&mnxSQ|t!T`cv68ON)2E)<5-AfQG5RQO*kW!eBoWzI= zAbE^|#-A>DXG|y(5IToI-bQ4oYxJDfS1cRO#n(A^2x1zBvNdZJn+vADV8$~gk(110 zC(b=}?3gof;X#^;SU<`{Cmg?r=HwgRcxx;~Q|`Dtxuc@g`)h+I9y+mnevL4^(%LOa ztEk(QJ5VlX?%+T6^FN(kl7wROTyXz!`uK^ipRy2-O%oVBSEflQUYiMw!mpqEg=g*> z1_Ji6*TNJz=r2@WI=~R+ZTtq-WQXN1sK!6U^cZ zs!G>*a+~>0HqW`-^kVkmTG!Q5-rV0xx({2%P@dbUWS2S%TOTW0`^uFi7sO2 zA}ZeTC2u_qqkyfs!=cmf2SLyux`8yexV!ew8`eH}{Ufth*G;tch-xwvKVY`n%*k`i z!$-+izDXZQedB=wdU$CUD+;Vo&`k+&$zj@udZVLESL!JUmS9e(`Q##S`R!;k$bF4B zJwFO#=pj~7#giw_?Af!hzP=Sl0R$#=_MSa^Mx)_i=soiEq5uA8^>)8OAU@F$p1l1w ze^=!*N3Pxe;QOw;_hE`E+a2coxxb!mByW2ATM&WF&b3^}z2W*BU@CEub@N^{-pdBb znx>W{4G}^B-z-5Jgz~JW+pKB{cF4qSX2oV}SuUI&A)es&neWEC)kC+rrS@ z(d!r608?HN@aDyk-vvV*9%uAf!tZ$5PND9Ak=UW6Nm=&ur+T5A!RdgO;dnO$Xs8dc z!V9=kFYZg6LYF7AW)#H>wfVYGcM8YzM@g2zAji|EEM3BuY^LwA-45jymU3Z^6@weW6SAFSfd5}1xK$lFUT@$Ux-nkIKB2p1|kP4b+PMT(R%F~T$)Bt4xonIVKL$CQ$w@Lg2hXF)wL>U-ym#g^8h(w}9W z%yr^XQ)>cd@lHi6xnXqr%=t9WIfV@40bn-DVyMK)byQiZn5ImpOBu-izCS`Q25_xB z(gh6=kdnBQuCKZ4sK1-gDJuZr{rBmQJht=fsWqQ}|3@!28v$wrkVFjy4%gFgHI8Pv zK{crrNrhoEO_8W6k0#_3dqNxOBTcU-BJkLA*MHD%q9R6h-UkOt@YQUyNzPx@Di#-aoo){vedOr<4?g(F z<44b&JHNf&&RAC0RD?^D4#xn0m$<`moa?T=uGiUm>X~O2=I2Ml;RoMy#~W@w{Dm*w zv%I?X{onVIs}3CeAAkJE17~p0J@=ZXcG+^rU@-iFzyA;Oe8*ozN&0~w|M6e?)t@zM ziXj;=DmNL9xr55EK{AA^=5l&??^R#>>fMva9O(9TXZy$-uNQLZ5B|#^{^C#m+JpB$ z{QL!9QqG(?`-gw@$=Ss#7xy2Ya>r%J9V8bXf9&bzwYSV3oRMc{WoeVAbJbJspyjgW zj$inNe~Pf}`6Lv}XWaXaH}Hoy+(uGkn!sq9gyOZGz)1hxL;w2t&Fdar8+mwN@UQ;4 zqR=&l(MHR#QvhnraK(xEid4S%4Z26Z)K5q^DHK-Kb-Uwk6rRsDaV_)WImA z!Z1tpM#3b}BJhpT(Ce*lVcbL2FG`YqHB{q-ahtEo*LYS{5d`_405~j@4RpLJJ zL*j|7P-B~ls6fpl!pYg(_YoE(O-*n!x}Ff7rek<`UQxu9jZzZ84yi0C3f}J|3}?+; zsUsRtB|}$pLKf#|gEZgt)wpzM&w<*zZ{B>{HRl$}9aBv)MnNoQ`3oF3a+$}TCSSWZ z+q;J?+q8S(CXm`0U71K>1PBb@$0yP8-MEtj1UP!Km@e7^tXUKkSpqRcY_fV`y;`nZ zcg;0_@`*qHo)3R$ZDVt8Zg+RM_iG3JMoFL$F0gT@ThS zcxShQjm>bq8+IV}Am<_`PUV*AZ|Sk)tMyCK9&R{H2MF{4iD(^KidYT}gOdup1cCs% z+J@1ZX^vbMk$1|5XgBN!{{tNWlMGd+tUWI1nK(%i_DBLkAFdF?kQ;^UD9)o%=uh@; zz=ZJY(ML-cFHjx-Pk;5;yWZX1)6`pX3k?UyRg}suW3j2UG)YcadT0QO%orA1gExt+ zBoGOV4QVv_6QVj)UL0*Q&T4G6axR8X$s&+5r-6tnvW1{Z68cHw?Gh+}%L_VKv>6}> z{1^NP)1p%v;7XSClMYy8A_M537$(R=;9F2Qpwv+^0_L~$dXgop?nX-Tl0r2}Cm8fY zCx)<1W2pS5R(;6?^0?rp-1c^VwpDtG!NBHmJBWk<83cKtv4$nnXdIF4cF6ciUN#&B zjbK#3;FwvdZz8OPt@>$jy0;9*+-)u)bU}>g=Yhh(Fm^%+PB1sW@s-l~7jqJ4B&Hk5S>7BUMoW!;Twdkl zr;Wkt|8Csj8i!Ns5F=<(F+6AvZe0>?IaoflcUBWpE_N=SKl#kD$4{I*n-Lazm=h#u z=i{Ir-L!r%yd}-=qKew}1bhuiT%ES4B+kKpA-*)MvtjIg_D31x5xq3Sl>9)N@%m z%$c%MG!+}*kABXJ3=o2G2{OL`w6QdgD#oHDm%z>FsI*8-i9SJw{o^zAG+BCC*&D>m z&s*@W+STE9MYpkjb^I^)wDyM`+=;R}dvl9k4b8-EI z#Dk<;(rqSZ2i~wmNRmQj(~WgdvVNX!ha3H)n1}d++QpP1)_?cnx;Q-hCgL zpCz+1Bu!GnA%LLE-$h=GifB9B@=_|H5|SB$PE#Dj9MnP-3J*NwpE!}{M4YSXwq-Ty z%}S#Lt=4QE`ux}C{_CGq?t4T;q=Q>P&i?4%=l}7K`FGt^SfYRO%(;SBj@)*87DfHR zvkx8JjnB-_Y4gom!bx$z=*Mhevh)#MJ0f*GZyD5|LGi0_Fm63eeOQ+&s6p=M zB&(~40qL?nlJQVvm9h;z0cKeXR$*pvKtRx(u~1jwj1+(1M{YXW3O9gkO=V^)1hYs8 zfq0S&e+hPeYoQ20;eugA z0i8b5RBgm(QWq!o*VFHp2{KoX!0>2Ayf^7dzhqL!2_d4 zhzICi4|;uFl_B0JoU?325RAB2u+m62FAX8kb#Q2e8U45iBA_8+5ob&SrH)QUZ)?1U zy8yJqBz8TQO)=~uq!22PE92`>=a5*qNtzW1sEPtr;-KKQOSTsTK?*^H7c}4z=nnQL z9%`(wOCX5v{r;|@XtT;ZWB||7%?@R$s!Iy)Qm|Rf_~0*<)m5e>u@uj75$HydQ3OVT zo=zdGs9aT8-cWldVq=DpWe)K%{b7ru0FclH?;MR5bl&vIfJQYCQ$-;%^h|36El%oq zse{iz`zfMmi&Z+K0Gi@x0u=NhbVymzK~saIKa8CPtBFR$864YLD77XVw8Jd!M?CX4647m)t{NEz2wXeD&JW)BCsz%i3!*T_MaAH)gf)4Hzl`Z zrLY8%MZ@Wa=!A*L_=J}*kJ~{XY5@R%I45TkMPf}+2Tfo!0izH*e1A}S>Y-t|^=K-_ zOn%)phYlWEs#O}TR%?D{4p9n1S=aZW>ky4k>Vs})8LCx4J%DQsvL9YS4>X|^kD-@U zy3hR;OV{1_-)%a%KS_2d7tv)!FswPv9foAAoo_eicFRVnOH=N6&2R_*V?X;tmnNb3 z%Dv>|DqRt9muRbya(rBovzZs?O{{@k6?(d;F*!)}NsRZPE79z8r3h3iV=EGsTU% zOp_--f>HD46$AzzUALkHX;4?25DJin7^AUMuvtzY5u$Z&I)b*Qda=k=!N|R0&>qh0 zo5@Qt;tlj;H))G0z&2Os>(Z{7_-l`m?KW34iJ?Q}@r16CNl_(M?2=wcB7`C-Q#j!e zsM4_Nx~8TiD&l~|NyJLqao$f@FO|Yf_fmxvUOifeDUFQgrndzLhsJ0}nJ%lY<3Y)a zG7XgsQ!z~$;env2tX4{da*(S&tC%HW>7e$`H;W&7TfE)lmN$6lhWj4{ZFjCbU=Ws` z3<&8W3kq!2&7E`@tejy^JY^q!N?zZRpE@r5)fckg{sZCu$HhmE319jK`NL18-EEpO zqS;~}ezNi#zm=a~m8m=%mjnCrkN^7S;VUADThq{M%=UqKy}EaQHLba~)5(W6)^}o zu2$6x%aC*kTUfR%=(+%6DRREu9eG|*E18NcG6g`MK5+&$jAM!KP*`{|{zKgXejLRq zf$)|B81UrpFl_29Aa0H;3w4b*+``@7>eG`xjW}ZIW)PUD1m@aMR@Lz>|tvk$D+kL=X;z6Xv0x`d|%6pr+EY zL<^OH8E=Q%Ih&ZG4aQBN#uP@^czWgqI+<{Mt|EpBlY}i0`RN*KK(H!u-QH_T$K)trGx@ zFee~!O*1DOtGUS-0nF((8E|NvFol7zg{6CehLKa%ZD86ca=p~2#|wxGaMDS9FAjj= z2rvu@3WZYeq|nJKh{^Hd1#n5HJ7_iQP!T(SzNDlCS}fp+V2QLhRvqSo!zsMmv>l#i zktv|Y(dwK_R623V4I_We-AwWXmJhNR#63e+a6j6UW8=ra9Fe()?j}+6Y;7;ANk@)c zX;}#II73%NNF+hiG!H=Uaj7s zHGZWs@_JM50C)Jg+KF{HGef1tn7iw=7+nHw{*hYv0NpQsT*JpfegS zgieCmSA(tI#YY}^XKHb{WT%2FHJh@}#%<}L2=I>4}(rmSU_-8*>r3r>vOD4mJ zb<601_kQFZM5SA? z;MYy9dDCroE?jr>`QuLxhr`Lof*|8L7J z&GAXGxD+a%G3jQw2|~h~fK%uXdj6nf;@1cR*>x5U%s@NgYPw{sd8?Xi0d43!R#g|T z-JL#qF?sluuzyalEP)XvR^kb4*mm;~ou$e%*c;Hzl5Dzgh8J{2qg%!!j|p?J9P-La zQC~R4cY7 z>v|AmTOB9NvYT&{=jzI%Pf=hwdO|1tXAd1pB3>~>9UQT|&i<$W#Q(QXw7&6B>F9C! z_$ltOC)s1qa%B5A z4^>t&ITi3^3Q!w4EUc!$N`-A-<~im-IZ`AvdTee`!x%e`OX75WduwB>tEuW>FdDf- z_@N|F`*0#QbwHkpyU%zAc6x62~bbKPO3Y(F<0#h2(4aAw_Ktw0hNQIv%#UErN| z!wxKLUSEib`dB+`sr;-C_h%5;l9gWYiLWvxRr`By4`ypy!RYR!<2PDlF%A=9E8t2B5uxmWcOr{_@uR5-Csfysb?1R!WNUh#YaHUP7ugCyn{YrhliplxkXPnrV|b%$kX*R9odo+)eUn> z96J|zuHc=?o77$zlIQfPI#jdF^J$R>aRP*qc-f9TU6fpP2Ei6^dm`u!!O9r;I0_4< z0Q%!!Ktxq(Eb6-f^_{r0;w=OG*Mb!%8wxU)!8{WW}Ga{Z)$V?)^OU1!Op@;_d!F)W)9wnI{rEv=8BqgcVk{2L3 zV#eQcda@w7VOSPx_>(HA>*2bexfRtwB(QUJL?9EcrvnNzLeCOe5c8a#Z2@W!Sq))E zC+ri3tEpxD32fL6@GCtgRh6XiWV2`7|z)%aoo;22y zLdwK$=E;SEAOw{-y3HAKG+PB4Y zYtU7_;{u_ZgxuQc^XBsDN94+YBe&dol_c(n94Ik|>BpigNSr3fx+bzjLhJ_wc3i(d zaBQoT6oD?wwqzkdsws6k4^c4ZFRYd<(=JhGe8rLjf~VZ^E!@%fvQV)1?AdFUYxg~R z$_majnRBsBZ#Ll$j%Q_WRa!rK>coW;$4))`*s=9fPi>re>b@t=9$WUT{WrGuTzTS| z;}0EMJNnE8#<{p?F=vlI{^fh0*>lxWY5!G`IY*~8d3rSXcjAsMkMz7j*cr^uE)56i z<;fkN{JlRocJzs{DKYQ<-Vg4-?#PS+u*Fbkw#00E3V?%AoRmu?M)WiDtdlw^qcDZ% zIpvax@c4~)-2R!*{#6|BEZbSyxBFwi`j0>QlRs2#mUEKzqaOSc0ZT&Er@GO>`I#U6 z{=btZ$&*h#J^4J!Q$g?h{@G7=VsFYFmjidiBW8JJ&EB;;$;Fa3<&M`3ckuu8=YK|d zE(rx9(*N{{GAg?7VE(Q*3;HyH(KLb4Ycqk-L(BTVd-nSA2r|F+G5-3ir%5QL35;I< zNhs3m?t1WOW$&6?=M{?=4A%S=x~!VuYBCfF$M6zj z`mpB;wX$87SjLuY$PV-$WPzJGFw0{6v8Fa7( z!~fz!5HdEkd3%(M263+wwlUloBUuvz4<0GXvS^9sF5OrvY31N#P^in?z94rinmuP! z(KK5U70?`nSCYdCV49wLxY6;I zdVkY>;}J!YwNvMqpZ%3=qrJl}FDa!GdCxn@JMYR36A&V~&d&QN97o>vp6nw(5g)#` zXV0wRw!6h$O|h!6D!fL6x(NGB9wny938S;ejc1Qj>%H|&Oht7x(+z=<%Rhc1ie)?EzR)5pxx;MF|3-F3XZC)x~2{~qo8!a%C?lWH)EI9 z6S<*@vzoXbWNn`*D_mXC+yu&#n^93G2(S!bGov!SScxL5AW|nN2N@BCkY%#r)}U3d z8er&g*Q3CedU4kf%%;`?Uo`cWK=_heMH8CpoR>);h(%Lg8lQTLZxJ`}e($sF!NuU6 zS1rq%gLVJWhaS9UzPR=3xvk>RfaR7Q0&ls(RuKgc-MrU!*VnfFC`&SS6a`tHOSyu8 zHAoB;brNzT!y(r{v)9$h*{HM$mZJiS3>0%78U8AgH|^HB!H zfX~5)DLBcZjDQSmUGR~~)@tmcfap#japAL>9J$i=YTsySPSKfcwML^%RVkV3m|1pk zds2WBs;J_voF>49$|AZ-SKY>G%CS(16~SAxd(Y@};;fR6m)566Vu>X~G)BpA!UBLP zsJ^}CK2&baUqxsiW&w0Xn1v;|jL$2g8TP|cU6a6FQPFlB2y5ILKHk(Ch2*!R_Au+w zqz4)8nJg_7S%?$LgSbB84crEJ7KQQ?M=5ldYs__r=nM_;pB{tgIZ>900wq3$ltgNu z0(7RzjH#J+KBRXRv2M(I_(fn;+=>ws0U#f=!}?XWY#N*)5^cPlp#UT7x31U#;c(Cfq~e$IP_Y#3ymxz&cGk=|GtmtPDfA% zHIgDm$mltS!0%0;>4vPOhV%p;mV|y9XNVVwC~}>lA7s%Wa#0^#7Xb{~GE9aT?uOiA zaraD2lC0hK-R&+p`^^1=sCC`VM{YhOJa?i83cLx9pAo>apD?k6hG@L0BVgCy<`8CU zYa7i<&4P53Bp52^G3OWeUjNoNA6}|8EJZ2J?YZquX1RIh zLg#^J*1A5q`t}>RnM1vpQSw$#6_6xsWw(SPQHWlp|(72&8gG7!avnWKsQkWMao!ZPs?P0T4)>OJ#$F{Av zzwIr%_v}(^<$J#Wga7i^e!VhRZlJRXXG;~duC9Phq>ElUXGn{NfO5wjx8HvK4WIhU z&rR+-9F2HM@SWk5J1z(AkW|re;>0b~vQ^V*1U}`C*93R)zy8a=3bU9bp}7A6|DMO` zocV2UE^atLrU{Iu35;Hg35-r;v%hidNIqVA^H2T}|EAkH1R>KT6w?Go=sW0I*b==l zHK*545{mTL6Q7Ac@XNm=f9Y=Ft6$%F_Js1`_wb9eI~b*Wha?oQ{_LlX;l*y$5qNP< zpNH-U^B^H{O)jy+Qyy>kSXJgiL$pTmC}&c2j2N&M+TScuc{(&0RzMAXSWjs<4s+j7 z%#u_A7Nte1@p?;}1+@BpP9@M1nv$Q{V0x}} z?S3*|{RfDEIU`O>RJ0TV5V$|F(kMfV3ZXEF34g@ zcIW4!-o<2(v?WcYq&y!KBZbs~Y`WeGtRgp51ZwHL6fSKuJshhj-GKu4caR7-Y zFoMp53t-l~3{Tl!`#nd8j+C?uo?t1k^A|k}MnupZCFc@hJ;+RnE|A{&!=ykTu}oCtX9oQF z5kVZX6|lpYlodweAuZ{ncnGb)4-}I#NfT{B5FM~R2)oqfoXw~-rw+3=eVjiok{|-8 z(J7D|Q00?1Cj}HxlB-#f00xVK?&5(-LskVz0tjVgDb$x}1nQp^nIcgu5#=co<)NEG ziSB?H1x%JvAmDjnJKO{Yvm6T|09j+`=`P?*9B#YlcHK1dl%tz-@d*T|j5KG=IZQVu zAr)l-jd6;Cflx#ni}BPtt}j!8rwKgMZ02A1oK2tEXWaYVBC}kE@Hj}aFph!YNtUaU zSmovw#?Tmh51*5o-AW-0fgjWXypCfgAp}(p9Mca6 zGe&I?48?_-q?mphfGcOzS^5e55a3%D7XgR^5sN$)y_4(Xg)D1ZTWv!F@Y5M- zu#7H`w^ty3BY45)TzqCIt~v-_U?k{AK21C?j_6hpra-$!UPL$4j4z04;a&jVtUy36 zlw_LrKM139=T~@6EEmDk&m6nuop0Z@Zx`*oOA1fbPcoSYLR3a~!_!j&z`7a3rkZNR zan|V#N@au-kdZ2Y5Yid}YQ0v*kiW{uPeqbUxr4UuJ8*~B55l-;G+XyPaCC2-*G+>} z>jH`H{o3b=z4y8|y`7)k&sLhms+z4`^@TmN3+0-TK`D+#5v*gx$dRHi9XfQ)4f_@j z9CW!EH(@H~tKyE`rgGuwqm4@Cs=W<&=v;=}@%T5t^e2D($;oAJx%sA#{n!sRp#I1A zpjL{Ca!gfW{6rW=YF#6TtKbfYa)+BMI%4e|?kJZ_*IaYeU2neQsw)qxbT-kRa0jXl zWJ_}HjFYMYx7U1*I|R9Md~J!>zk25QBYUt36wy4T`)1sbWts>8$Cx7uLCD+pWhZtNw3MetV@ifK@M>uG;YC%1)C}?-Itve zsi$XJqm_R1snnL%O^13l$&)k|1imKG+^M!$#x?UiLtrEFA}ZXAI)L#5bGtP~9=Lw! zhr;l&%;^t>{fiX?TDBLl=NyD5kX)fIDJw4B%?PNobqlJ!q%ebs3sOcOKi|vgb7eH7 z=!QMiAG)d?Z70NUR%_Snt6X;#dFUvKW7_xqowFmDTi=lX#LqLgUme`Ix43qZ-Mbj? z-W^sdktKjex$P~d(WrHkf|ZMfJ^2mSI}2C!m+GW$jMCJTa=t0mDuM|>GdL?}69h}} zEW5my-F#RrFIj5!Pd~$Tx||{xAN|3+WR*ccWm8m@$d+TCC$p-qiFw=SdJ$Q7cqdfD z*zytuaTg~9NuCX3x317&5(Tx*Qm)hShl9A^3&Jp)o0*xNX+ZX2Wbs$}FJWYso_xQx z-7^inUb8qpkMrD+=@4rwU#XaGAojAFoUWVx#Zv8xQPIb@=H%vhyElCpH!)87pYh`- z6v*<1&MetQgI)$9fueJE7!n?eSn}4qRYg$g8Ct1dhNtJO6Gnh0ieS%`K}>?8*l!_N zjWIM(AldQd@^mOXj&L~tQ{&o#z13vyDrK9ig9l4{Me6ay#+Q~|Tk)a#a^ zA?V7LD7E5h%c!;N+H84YrXnj62_nuP4B3(<IgbXdoj+V0r`t!_8{*Eqxx+Gbti3^IG~WA`>S;(3nP9 zND|52^mSd;ZP|Ci+!r#JK+xH`sX%`+EN+TRNrC_b06*5$3J^FzOqS7b1%Cn^8j#B@ zD~jaINv+)x27Ttk@6IF%VJ{nJ38WX4$&5Nv5$od&a+asuf$}okF;{DpZENYuE00}po;h~x+}YDlKKPKfc+KMVx5m7< z%MACHioGSSq{W&V0^dxq`X$^EQcfxc8~%nO zDC3nlbQYG$IWGctyy$t^C?R1%=$ctJ;l|vNpIS>EdgjXSd;ecQ{Ybad!)MYoy<%3s z`E4JWa>w5|cfgcznF|+J>-E+`Nu*FU<&M_^ckn;)b3dYtlTZj8_n-biO;UD#f!zL< ze44;$n!pGI0N{JABrqzjJM`ztP5;NAo7s2)&4=Eb-S~DgO+wM1CNO$kC!ydTdEzJU z`Ow;cM)I;m4(=}wUy(pUEG^_e`9qN;#;=%!;?-}yTOOQAnG}qR0mT48Ok|{xxt#Am z%h(G(t9BzHW1_MoPND(~EBFHL4-t;0q3cLN5PP7L>0e=fEGN+=r7YrsPS_!Qp>f)v zGxU0%u*k`pq6ySW-E0KhKy72<8={s(f-G@Gk@u3W-JCC;zDUA2++T4shiB=UN!$*X z*G1?@bSc-3>1q0N+5+edw?h= zR+Y5?f?X$!LSGR7E-8K9xa!Sf-?d!uAziqx}#g*$PY z^v3C%@!fa5+5hPGXS=E#LJ=>gNi8A*Qzfy;(L4w}#phDZ-Q-00Q~CG*kdI>)wDrMvfMvLDr@MP@F)V^k z))9rm7By95N-|SY*t!Y|bz{B_2T5a75M%slQJ;}^Qa4fuSz8_PyaJ7=X^Le)oxY&q z{M-XL11X$z2wk|n-J74A0i$7PR9;d=5!dmu5VYQ~%W=vCnO;jyMp?n?^N5$|R+LK% zmyH$S&ZApMt6>%hEDw8y<=_V@z0(;96(c4?I&B^gK;t^E=L|`s0-raO7i8a7l$~|b z1sxK|8(_F4i>j1IPUhXA^xoU9L8k%{DM+%8$WoFd=yFM# ztZjtrjS7d$=>q;iGOY4CMLO6S&x@is>a_b@sU7e+>v(xI9I~*vQk5=`R6)!#W6DVb z5ujGow7iCYRY2_OjD-j^%CZ11i@-+_NKGS2PR@=HdGn^oX{xchw$`XuC$z(JQg1V~ zR4WtXemHos$t+C5HpDwO)yNURF&(1G9Th@9EFZ6Os5l3$t+f$ zcoJ4YvsE14%c-p04@bZmh}G5~Gxq zUCidgjJccFO6V|<&dO*5-vip9fp}qxC50F{lS0MOyB9gM>x^EPg-ZgJv9QLc0V6xV z(+39xw&5i1rq>yxJ*P@C{Es~t^b$lj4m?Q9mp8Mzn_doW`*)z-O)=F<}d3)vj<0sB1!RYEY-dWqfghuQ% z=8}R=5Eua>Tp}8qD$!Z%NEioU$S58~ zc?bav150CYM*-BId>5q?x6;YTN*eB#;F zzxx9}$_vtzJO0MGL*&&EY~d%ft(qnXQ|@?8a0mZWKlh^uhiuV4yFU8d7is3yr91V-H}um9(NUjO=oJMDVMo6;Zok^I6m3B@#l(d#-1#pv$)zyDM3 z8-_ISu9nGv{q6We?=9}Um3z~j$(={|dQEvXNhoU8EMB~t(sSBT9@OMIEEFE23L2<^ zFMQV9EGh>zSqSO5VZ@SvLbOUj9>^Tc+<=ck1mI6pTXXb$K>QVL!8@?Y$-lk04T&H` z(%C1@3nh8iu0>m+>Ia{M?nf7eiKp;7{edK5IVT$x=n#>$>}K-lNvUK7Wstw3OXD1o zgoUof*YU|Dk8%ByR7SOd0)x0uf0b=h7KSNPVcKl4;n$XWyDjvD=$`U z!SKRB(7Cdz0y&|NbXbkzaQ;x${>3;>C?h-EK3R;GC4ew1jj#oeK-b zujrgG4QIs0K6=cm6eanBYM7!P=NVcWXW7O&U41a$YL3_c(uIeU;Xr{4Zv`9Z3)m`< zgrtJ&I<-oLuHvW9sp;}bm@9@JVG^PU!%(}!kTk9|-4mH$>MA`cyc2FepD2Lv0Sk@@_OrS)CkTiNoM8E>U3yWdqPP0$l?Ro7|QFs{ps3Yq~< zWVlS>q`Fjw(2VmGQAC>2X-ou`>7ZW-NGGO9T86GDAVY0A@a)d>|dj=XBmw;0M z#fh#MFvE<=RY}M1=umo~1Iv^ZbQiPkv9?4_+;QNZ90(`~l&!IL+1)gqZMl#} ziKMn4Vnwwm*ASfwB3<{2fzv3_!1*|R42*S2GZN4m5oOq;*RG<<*WH~tK4qXO?Z}67D$SD({KQvA^I8*0Mo+V`K zs4FdQ57`m$FD|%PfOum#*imM%^=K@?s(5ziiHPDKu!6O<~!i z;_H8vKXDKH>{I5}g}Oj0t%@DT&QtfD8TfbHaqF!|t`*C2y|K8sYyRNvH|@Lg%|g{= z#!6#6ul9T(!Dg|XWUL#9Dc!w5g$kW(nIt^H01}`LU4sB8_qL|oacTKZ+=2i2gk4F5RIw_C(l9l5^j+kROlvq%aO&8(BS)@g8S!%C zj=s?Pfgk*dp%Y9l@~-#2?LF^$*W@(?giE+%n0xEdb~haYVGs}F9b6ED*KIqTW-4Ru z@Ut)hcNE|b@CMz02JV28?Zo-nQM$*HYD(5iwh@fGv6HbHb80<_!Y{6#TH3SJZ?CMZ zZ{xBEeBwBI&%56-<&M8`?w})>XoJq6)SOiWX38C}3GU#3;-7qUl7wRUf_V4+G_tzp zs{GokhtmW`(*#DZn1n*)#mgaq(fxz^FaD0OO*i?$T#Ju>AN!&2ALe7? z1V-pXT=%=3gdz^OPk*}eYkzh)%%~IX-JSoJf1g_PvF-okBowXEBK_K{C>twlwrM6L zhU3Gk(M(BD_|e&{NC)cPv!gsNSSZ@M3fV-(4IGX>_snQsR1_EZ3c^^7(o(&0Empm z0$6O$GqaPYbN9>f|E=x;2#6F3|IbG(>BtTx}akT8v4XKdN($9>2zFZR$BPE42GY{82s zO5~0mQpMmz5j4y>ZV2E+t&sR&xF892kv(tX21&mc6f25Pf{gQF2omJKK%xTnH^Muj={zaC3#RB){+w*#}y>)8J;!T^kWVo}z z>CpZ?&(Bj)CZ?EZhG-zFOo3nV1A(%|eiwTyX_WUmG0lYOu~8ro07O#;i@?2DdU0>b zU5v750B%Biy<4k|ATPZ*4BRB8p(?RyEXWW;5)mYdE~v)FvV*y4>1)eP$t?B~t*lr@MbSB2f(~V@Ybuba6=Vxe zvKqt(OL9#C?!@)9(Mx+^Iz6-2FD*V?pPf0h|1CGn(9T(|d@#57vap225unC5VC`EA z8!yTIFl8&#!TXAYO~k;Z=Oh!1n5JUx_Ef2u_XRl)O{g25dT9Vwp$Wt#xJF>hBDH`0MBm$A3Zc!7!0v~j^E04H|SkEUo9iE7J z^e_oyH>V+ljU2)El?Xs4b-BP16ORY60@VyzLle8P7o;&;4mMAKLZIDtBkFbn#0i)# ziAlTVMu9A)It^u6p*dwMWHX*kQ!fa+QP)cyNv6PX)CygLE)c-0gfD5t)?-^uHObY~ zrX+PK?L+o>y=1I3Zit#G*(O)$zHo89vD9B(Ze5 zBPfdUJHPYKs^u#C1>7+RNY-D2lXRl~kUKP90mZPFxZE(b##&3#Z6{GnGFzof<#ccd zLJKGa?&yU1vXiWb@t%TQmMOTSnYNv*kD6YXR%fI&X!%ZlXQ25G& z>`h}ea_5bkn^0_SVDviJz{q{uf&cnw@W+2<#Dm42wvN01=;yjy4(6MiP;72s^tXEx zickLc^b=pHC4*;pw{6M)+VsVJlD17IP zL(EyO$nnnJqFh#?L(>QiIGF0lhAsp($x=3@j$J+yhj9uRYZ#Y!9TBMTY?5}$rE3@J zk}Vb|3X}?fdZv2@WiXhA;8A?ePnqh-A?$Eujp`ZAh0$tyb|Jp7pjjntY#epN7dzLW z;Ji3WJ8r20lov8OGG)sU*k~9;S(-R%a3wG811v<6FDqgM0&{%OKvTqih|lRE$hRLb*0&yV9MR#nI= z5tFrt{V4DS2hlDLk~tdxvqa=5(s$(Nn$f^)}k9o!6+|P|@&(Kr;8X$|yY3{Q1fJ;RqO_2mblBBZEp4Lax4SJlF z6xvjfmKW>A$(>U7Y$T|nQd5K%ITZSNL(Q;(Z-7S3RoHIyo|Iu0($^~0gs2@qzYittd_hOe5) zi)s?#EUVQ6El~>PTB?tT);2^D%mz3mVTKXl#>VFm88K`{O7W+)U>WYkPd)eqT;Nu7 zyD8cNmDmWaaUedd{BBhn#pNv6?MGWY;WpF|PuIw3Bz z6Lq11E!jlvn@z9fBr&f9-98w&*>)QNjcC@7nt(3dO(a|nY>rGS)}nNauyjy(^;mKtFL+GsJb z5STh;ZueVC%kRI++fQi_Y;wm&xth2mQ_Jx5&mBE;?csv}x~qpf5CFgO%m4K24?H;h zILiO^yZ`5{H{HDP3u5?{+yR9ua=b=0T&*^kNryK9={v3d^tcu0S;I%4in=1W5ox04 z32laK`idn%gLTGKHi#!c`evb@`#}EwQvd9wOS=Q%ns?nq*PpJ<9GG48&Y!=4x1kLh z^~U?&_nyI7zD?QWj_(6@lO6j@JQq@W1sNzmE2b@^GqPf9@+;!or2S z`z?L;DDvh8Mw=TLUBw$1Wp6wD`=8_Q`%-y`K0kcdxnKIZT(Q*6O(>9_n;RIRq5igR zLhCB2AIg927ebTi;f=zxnI!)Mn^4H|#sg5_wKz@}f>MydJn>*bR+gls zd(lfp-X0l2j}F#0Bb=8~KH;B%6SZs1W90f79@6dGG%q6qPnnH8iMkr1S8o1cox+luI=9$Xjz zBuXQVgO-+Qe^E9nj~z1`ErKNd@cYsT)E)$$(CtROAV{pNETuZn##zKX<9*O+l8FF! z@FO3oly@54UZX8jsbHJ3$(jhAg{(&a<2!OWry&b28h65u8@s9^rxA^N+~oLZKkg2c z3e*!HYn7J)RN^1gD*;X4Gu}ly|goUY=_+%4q{gl)pJjr zY1N$@s-qM2K%VMTt-w_dXH4#Xm(t66JUWg&Uc`&w=R6q1Oaw0hhu2u>L0*?NsY2H@ z@0^{lS!qXc&WQcXYWtGZxgd5f($+<;eSx$u(#~bRGtWCq#9JfoYVNm5*yWOliySVE znNvpW@PZ&JB1wc`jrJ~SX|s^bN&WM@v#R&!3;j!`zix(2+g%nCF5+u`#-6EVsvpV# zXabxUS<2v5013=P7{wAV7ju51{;cgRn~9Uk<6_uQ62GBV*>9uwImxfgj1My{P@E)cA7jpbeLAOG|o~>ELf_w z;F8&Xvf@Fxr6OSjJl`c35eTFg(Vhdg&x1G*RjNx~D-FBN!VPngw!MUYj{9j3e~sAl z;z2}WAQ*gvrRhaQ4@h7uLObdcWrUX$DV4&6nw)HDR;NwY*Sejy>k~ECxllkm8|%(5 zDG%kGPjUzBAeff9xU`HeTV7q=w|CEuZPThPj*M0&N5*sw_ChU`WEnZBAx2bF5XC3& zD{a}X?mws*!a%&RL0!eHD!PSif_Uo%o-J!-&6as7;u0rEE3x%MNFo`eLLC50)vV?5p5gkp08Bjak_z$m)=u7CV- z^Z93)?H2j$H-6~&d){TI3ZQUv6N=3ZjQ;j)LeX6)KJt~?>7)9y&nM?k_aAe57n>PjJpshDO7=mQFjISsV^03m?e>{nJHw!Q#lJwnw>T(lcL z)L3ZR$k&pIW*>fpeTs0c1Z@i~DW-9(3wlbEM$`ytq_G*RiXsUipKCW?OC%{dekpnG zLiq4=;ZrBW$6g4ZJQ*E56Fq%eo$YH8Z&gMty(keu6?GhMuAj)cYkIRCb4=nj6GX;w zSKK)#b!KW){f?{2GCt%d9U1&3;{x+u#Ic3lGP2^)vrT%g2dqnPtZF4SEhzb@k>LwJ zt8fNG1~@Z zAxefUEjenO6Zo_#3&xCxZe+V}BQ2=r8zzf>78$CC4OucEd$4w z3o55cf{MJ6MdUS(C$LAWDP{M(aj=-5y||3F%u=DhT&-d4ZcYfd9qlHrZ!bOtg(v5FTHZ;( z2g%s=bo4N(9pI|_^5Tx9u#+o{(bBHe8g~s#C~PI>c%+tk5IBYXvAvr}c2uafHC@R# zRn`6KzI5aUbL@6rvDD5<%~|1$9SO~%7dNbs2E7?YXd&J9&)hX`k@N5|!L|suqfZ0ykg|*&k?Fw@jD4%AW7y5>#AaL2*953!him2FO?JO&m zy1(kvZm;7jiX4hPgbMl{ajhzYftpn4haS>D;W8QvYC%(=ZY8^?#vOeK0%}98@J*Mzmt?3cf);Zci%tART{{n;=7$}j!z z_-$A|_`bLQ#!tO1q-LL!+0sD>1U9JlGVTzwBqLGCxC2!jyo@+!Gt%P(V&4k{%?*sMz734#7PH@c{NUUI3%{Zn{hN<`?V3CGZf;<7 zm25(>xq;FD-^_Te{e9XRZMVehwE?!ZNUsNwGNnLjU>Kyk| zKa#!g9UC_*UtZ7%|3jNln8quX-}(-v7p_aPw79aAMkMmmzMD$T=TgxKiU*;}S>&by zq(9!-NqgV|l?Q#Y58~8e9-`kfyn~2o&SrZd_Z6x_dZHWI+LkoaM7_V-Mb{R~MPabP zAA90^ur>Lg*-x(u_2Up?=pj;+9EJWDa9dw5g?2 zSwh;g)dl8P;>S;MM=u*}5ey@fgkF@XT&R1OxY9mVuy8Ku;4I6=)k(mzD$4zClTV>K zEln;gIsC;AX@^`pqH8|s1hFNNokebkO_P+z$9O;-dn$e-BZd>-xLnF}Cc*1|r=}D# z8m4T}B};jtn5yUc?PkvpV$csPL>Ncqf~ryQ4)K#v=QW^4Gwhsha!v&1az-?WLLuSM z0|B&AKk;%JBePNfESGTID=XfvqbFFS-*8YgWY4nMy8Addr2;}5HDmX52D$)oLV1LG z-0k`+tF4Lg+E_(fa?@6jj%mV%bsH44$)d9ma}n?oFCuH2L8l+NIBe#_3(2NxwEPAR z+=&@S4(7452@9+#G6D5uelix2G5vn10=fDZ;=;tau4qX@kRnzhXnL?PJNjTA{~_$XlH0dYhgF*QgPv1S)6AP2ui8;dQ(}`H{sh-I~BxJcDKqVvgvn-0EG|l`d4U#-S zaEDxgt4*0`VAErfCNz`MH1`u9C?uKIdK|Qq6xM>bCndJGshe{VQ7mHb5Ky=FDM7U| z!A^2!1_ZXws1igenuNIrbPpl2pjci5{npaTeYqTHwSIZKD;NC41?{9+s;IQ(w*XUc zZ%{rUN2o9_0>fuw#64p|AraB{vwFU`8KGmpxDn zGQcrOEc)wF>}FX%3E{T`nG3b3O=6#zP$GbfjQ4U6pUPwwFJUa7KdomOH!AUm4v^Yl zwMPi29R@^otw`jmQE0wPOx2ij4KB;B;K0SQwwI8HUnr`kj>)7yG)6@b{2R1v;wc1@yA( z5YOS#ghnBQV$ThuINZK%#?aZb1(%oC91;eSKWSBwuo35jky}y!Vn@wGzIgUIa^eZT zJVtJPvtz;lWQt6CLFh(7FLGk)A>Sb;%Ssj3u;Q&PJ1d>Q4T97QQiB$GDFd_`qyVqR z0hdsQ--qRu7Gk47cMyc=MnPl^lP*ZvQ|kT5%kyNWFsZ({z{+OPlT@}Q>k{nPEB>8GB;t4E$WI{f$tKk(k|+nI45mQC(>t++$frQT{^ z^0LEQ$8Z6g+;Nq22mdR-{tGW{LQ&ynA3C$6+b8Whx&2K^N!#3n0x7`$ZEj$6^=x2t z^#0yIKX$m|uu<_HQ^CLcJ73;%Z7JEh~t)F+S7Z#*tO z^q}_0!{%o`ReIol>)!kIBS*C7jwTO%Zs{|B<^SoYjr$(aA9>vR{QdS9?^Dmtee+iA zO9gd5xR?B+pU>{Ti;Y~7|JR#P$P7_mRdK=Ja&8w4iOQX>3r^6}y3EB&VVdXJR)g#& zUDAeNmx%yf%L0akpb3%6PTWtR@_`HhJC0e1AobB5pc2G0h+G#8a9f&W>&p#^&68x; z4xCT(4AE%II;GG$-Gqw}p{Ttr8o(!XB#!4sKHer!W=@vf72nLgEoQWPv~>L;;lNhu z&<^q1ZR!4rV8%?wOs!ano;jDCnHOX!u&uJ_Qh(LAwn&3VDN!*^(4#PM9XE(#c85+D zvLNbu2`}ek2DhWYO`2Ry!wDNzEib9ks++C(bluM^i9HPstYMWxVG0U*E`@pNr5=K_ zC>D|dXb?^o2`}@arYl-OPLss#1+E(!mZ~W1MsCVwDz6%n7AF3h+wVt4mWdj_RFJ8u zsHh|XYi2xH991SUt2xbL>aKR^)I(1&Vw)HZc5frK(qLyv;MO4EUC$qcKf6=h@4M|z z*KvIS;7GNkYZ`dC73S*!fDAS%g}>^v5gN2f(vSO2?DRqpZVlm7P#|Yv5_2I@LeT{j zFbvPqhzpBS89s_VYe2z$Y~M^&8HvX8oGQzWR*zG4GhEWNLLwCBoum~(5(oS#$l*)?xfNy za|@=D=hm*Ya$Ter17HDL=p^v{KvHNz{C?u(ko6fS3vB!Y{ubcF`iKIEpv!Rr_5gpt z`EWRtZI}TgE%>=3=4|$~CTb$(gUoFPZHUyeW+Ql1QIgrCM)({Ii9pO50TIzSBjqU+ zBN!C_Gm>H@ShH!&$puh4j$=oG)xY8u#yXsUPUICG5;7$bxCw!c1{DMn=P?o!oY;%I z&=P3)p$P`qzuZ5dFltErFbzP}(7st9r4gUX2rIM>0s_8+gX_TO@Qv|!mP#^L0tJLrKp>{LsofYaz}c6#0~AE7uBwR8hDm9)o5hXD@JXyO}HNfe^R~7s6(O z)XHU}S~YD$haHg-V{s@#3g}CC9AQf`Si+wi23IUcSGJCd*$qy8C zZdKtcKryr+&l4@$TG2Hz(N!cUABI_B9B1yc*d}Ea-!d8mY#uRN^nk6$&tgJV6&Rda zr39{8S*t&LepZ{_Icl0^Rk9Ub7LZMmA_xUZn3+2vtS#_64@TGB%;_==1TaM5WnID- zY>WiQJHS{5__(OSDn$e@R+XwEs3p0?6H(4JTpuo<^(S;1yXEnsi})4`fgVQ|BM*Vn zERqLHCB=Qq)_Ux4>o_uQf@pr%LoqP7|9{$YACU?9>+`;#y+$9&!UmPDRjTa0OZF0v| z%^mzN{L0_M+g{p)VmaD!<{UfZ-u>L}N%q_^gPYAwDBNsw10yz7hz0#?wt*2m$Rl4X z{^334c<`+0oi_x(^N~mGsa=~J7;SD~bQNwwasO9~|N7tQUw=@)=WEJWAK;HXC!9Ph zT$rVc%XDo`@Om2Dq&C>p`65UfP%bV|Y1>=pwDMHIK^G=Bh zTSm=YJGn3nj-Hj58`S1{Y6(%G3L4HR5CPfRYWGCwHH|8U1Slx$rrTU5Eg-I&b%DJI zP8bKId8gY`6cxCl%4ijuL4yn|5@Z*QLnne(Qf-kf!^>_N^KAB}DoD6W-^+kbJRF*{ z5Qe=kKR$_W7%g*ey~`XNf%QFV*mKZI2njKXljYS_5Q1*#MccFt9j(eb7!ZaE1ECn+ zLxOQDUTjOeAwX~me(Lq3zD!j#KJ*Gq9iUyzCBx9-JjCVuSpZN?v&0b1m(Yw%ht*~sChafpD?W9>x>QNphBsqWf;_>It?%A-KH3tg4-7`J~5B+z}nU zg2qs9+$#}?EgfVXXAy)AxSqk$%SxPb?Pjmp>_83cx`tNLrLgFq*V8sh`%z_QVr}!G z>=9c@o=Y*hOwvda<*pxs72t6NE`!d`HV_+O2&Io8LGVM{0rjbyyKH_UYjYWw?B~6N zvUwSRHg;JwL2UFwsEH7A^==>#*&YzEQ9j&U|017dU;$kj8(Vuc@%nmD zx8lA=go3+nxXZph+ElCk*aJ)AgMbK4!Syonit6A$mXK^Q!C-@5URh983q}{37XU7a zKw$tcq9}?p4s}6W$-XxjCV^`JRK`gPq{SsdovR|!h*SB%$#9tgT&Aef060xOHlD%O zSy8=~FG5s?B3?^coF-9wBsp=M4G|wdoxbsQ*Dxf*6biPgfmxY0M#E;g7IRt%)uUvl zi0F+xy?;uII<7tCm!<<=NofQY&?vriL(~Ur>gB4a7_zPq&F9-e5`GNfjec(!Qw@vttpzOXaU@G!C+~f!wIRdQb0?F z{{z7`v>YfA@twSfKplnwVgrcb#{sOY6XO~*yb`e;anpLx>iW(oTjh7-uF0$9ktp;x zx#Q~Oj$WtLj*Mobv18lTN~QRkbH|_l$$y!fogW_jkN@O%w@z>Adx61+f?qevTY6bj z;o}iCmvhpLh|atid>IXqv4aUyAP{u+fI-;(AjTc=nWEx$qM}hmD-Dl~(n;FQv=t+9 zNpkt(+?kUXcWm80H8UeYV|I`8sl$)m6dhkeV<^|`WMjI5cJ}OrM;>`>`0=-Y-`xif z>>vJ&vdJAUmG3ck!0Kw_lBL_npF3%=Exd@XZ*s@g%N_i0{rayCKRn!o;(?R3E3>S1 z?!F_R7)20oZbI=IY(lZQfzh{aVAPAf&;P~5m%a>&WtIPt_a(pdwtJ=Wmdy=}Ha9T3 z3OAuR_I&z#e=4E{zO?{7;G``x({)ak!K)jwD~b93y}71xwvl^2H&#h+znT2X&&NCV z_IpWF(42{yP%Be>B+370ZbI?uZ$F(na$Hm-%Ql-{H}IlLNs|gYxXy)`>Gv_5R+e*F zkOuM@oryR}@iV2Oz=;r->^8z+YW-aAa#1e3soRUY)D;#_F0L)FLM4j1&~5e$BUW{~ z0>le4CxuR;$%?mLR%?Jeg{l%11#e{uUZ(+@maCxY-NYdrqY^K5G*M%>IteMTbuOU~ z*qz5TM=->eY^c1(V9>C#ZIel<%)f9hIliQlLTUd#ez00CN|Ge~zH9rr^2llasSDgQ zm&xT-8?|YBa>=dnG3p!5i^z#@|nB@h-hCYFV0HMrw7X#|6RIkbEx}OB8h&U}P zfbk)V<>+bXXZ z#&=6<(TyEV(AjFTB8;32u1AA|n+8fDZ$%v%N_m#(y;DJH*J?T&(_T006hp(wsN->R z3I~^mX#``b>adp#Q}BgfC60Ew87|q>Mc;$;;Zgr#wcXTlNPu@iKAnH|(hEmVzwJ$T z7fWRg;CcnNH*-QQ`1()dkD0E<>-CkKNonw2Y z;j)Bd+nm_O#I|kQwrwX9+nR}O+qP|6XYIYuKUg2u)o(prcUS46s%%$Bc?pV2RbH{& z-~ZuB8!o|pO|(Xl1Q+;A{7&`4OfU|v#VJ53-MJx|&m6^-P4}Q{IenlN0HBS?nKg zt+{VPDiPF>I(AT)faaAp5Shh&FSD-2yl+N}W%#6}{wo?pA-xo7AlcafqXphq}03KjVz|OW}8v#7@DD zcZWiSL+!+5XqN_%@hg+BJ6SyW>DRW62qvkcJ0?B1GMR_eR3!VDrJC^kmxT<(Wy*k7!N^y zyG3J#UNp%hcv%iwyF=5?!Qnrwa}J-Bk6*=@4qtvSq5r52u_hVlxpVkl;V*%5#SD5y zIjRvOJHjm>#_&C7ZJmp*(&X^&f4?RO-+7%`Vk`xl> z{p>%R`|pCPd-yto7X4=>Jugf7eJ?%UKMoPmb2W7r8{S6Xk@mXhUc&z#cxuj<>2vt; zhrq}GeGl=L|5@)_JT!jGSKI9++?xMo?iAl%a_7PK>|^ajnYgGwy+yJs74 z&9~!c)uL6kq-NT=c^Q^8h)K&ADkkT{zps`1&!Ui4Du6ix{xlp;WaOIq*tSP@jn#Hk z@6?yhA!uS|GVEzo|LLH~#J3KbEo+UFx6QkfO%;7Y%CeDJrv(-Th8Z?d%y|w2W;f9- z-Cd{<#*^X9s&iZZ-SC&dQEPFIqa=l`^Qksb*fl9q=3;{9kTg%|3J{4w3F*KS=;k2d z5k0q|&aBkRbwnD@<|d%$Nsn^1lTe7`_Nn?a7WHh7<#NwfH`Haki_yAqsm=Aef@Lf* zdcOho-O51Mt&L&^@`aCp$BEeM-o~wjtQVs5bY>O%f2IUCED~?D3jIt7O=#C@6heu zr4j|xN${gfPx)cUe1g82KYHs6oNKbCESeY|hq3teb3_q9!jgm64kcPpN{@tlC|IR} z2{18ny)IN)&^dLp=Pq8N8)j8$4Quc=#ZjnPU&m|a4A1Iai2k>rjMeoLF}Jj?W^Hxc zk!<*U9;_MKY~4SyZR5+tco;enxGx8Nv~CPxy6x~ zDu`_*xXMB5eU4OG^phwWIWkZtikBt20t5sZuWdUIu+7LN8KdOrgYD(DL`G3B;$M`> zo~`3>(`v-20gd=j{NZc^*}O7=a=NEgZFTx8bUOIoQSs_eAf$MFq2=?UY*C<|;i)Fl zhYqCzvfFJSh0ED&34XFQfQQhzN*00juo$M#Y~XhEeZ<58txJ9nO1XF$D!AU|3&{ra z1DxzH(2;yH$QKw*!7@Ts7ge$D+SBm}f<9;)3sV({O*j`_{mWEqE|23ON7!Qmti&U1 zG!`A;dhS1@WY7f~sKk^y#SOHfQ6NsHemJ3>m_#Hgj0uTmeyJu-#I{*zd0v^imL-Go z(;Ny^7Vyol&}ptke*L-A1`SSkG=I{d&~xLjs%$bQm%+PzH6z|T8wwj4qOUy9-I&K5 zEN$*>-r=IfGRLyB|A0X)+;6m(xA~14kV~WTnYH$*K#E!ms2-q3)P?E49P}o`C5}60 zhcQ996IvttfhA(+r`#g`<5-nc9g0R$M4wo2qTaa z&fg#yfk`(w0eg=mIi3zuKKO0?-4~6LM3POEU=WF{RgO5po!*U#EF2{5;hrAk zj(&S%}Kl8Nm<=*vxzl)Gg!`m*fMYB07E0PoQ%bQ43XJN-z zyV|Ds@MvxAE5KOo7GHcKw?2>mN@QufnNP*j3~ur*GXHt?wvubv;KkEa1>(Rf!ApR@8@|syyzs+^ zN-CCgF5&ezoCaG=kyS(m*DRk>vDz3mk%me_QXd+|@leTOibL&C#8Hr=oEQMMEv`+k z8=3R8D4>RtWj61hg!a$T1jjQ37mLcvneF3AeviY)Wp8ls5}zNzb<-VwPAh zL|8}q&{Iytvwu~#1F6KNh4WQsS(;0@Nd4nPnjAO zygXW~Z`GwkuaC`}cTf`gY8c~rB7&Ogi&5u^G*rDg6AlS;vz+qCf>Qy)I|0OYDv{EN zFZiu`>FLnr;qqwTv;~Xo@#>vwZ{Oy2G4iqB$h;)uNYc=&P>l|O0-nq=iVH6P3vgAi z7DOUQuX8z)Dl*{&0wwfe{VKkS$K?ZDXvV-0Czp(Z01*IUd#<;Y+Gjw13PqXwN5#x} z9@)W$>Gvb;Hu;`=uChoQBoEt!*aK(ZI=#{Yaq76W&orwwcPU7T1#o0p5^e8tJvB4@ z^s%S#2oQ=dLD$S#Cdx2!CME=Eh|sv4!+osS=ryBIq6shhS6^Hv%}DtUfsX()obEQh~>;J zY+xM5?dpqCW|62Yz~O2hyg>B(6jc+sWLFpkreq#_PYZ$_3j02*&=O@ia(VBhUHQTf zO&TMFJw5sN@=rY2lRJR#DO1+E3F;?d{ZIru$*~nMzCzcT+2lFWrH2u0=HNOpVP-U= zv*i>rEsra3xj76*Ke98SxKwEqPoaOKb`VEkppZm`5~djGnZ7(F(6E{yrl0s$;z$h= zxu;6qs*lCS(eipikZZxTf20n914oJN135 zZ;9a(J#ROhh1W@8*A|4NBUfw_5!ZmpUq6Mg&D28eY>*PSKNhnGF+B+q3TnsVEZ||; z=n6_TVHa8Zr-qS5txb@S?jyK=ghs8R24{ufelL1SyxKWAFoPuL;9?^iSq>{w0** z`o*}*lJLU5(&SmeA?cq!`qgNIT@WS>`g@s?bz zBfXi|bT)6^%?8KwzkI?Dko@#Q?(j3KC4#ph)|F#EJxlgwBS#pbDDN#1xx1jGGDhI* z;ASJr@$Mpgj+pf@Mkk#}sOCpi3Xw2hV{KlLT-lw>pLr%luy1>SzU!cr<1j>SqjPx~ zPsa{Y(1%bkIC`5W`)W5}o}AIr(r!Pb)WG}mc}xXfAa2zHa5-rA-tEfvS_eZ%Co2$h zZ3lpTNqvOyedG67_F?xvtDg2VL}8CWf|0W%^{xHGNQE=`2H0u@f(+sfz5VxZHQj1XMs&%`izZZ^2|4@%@Yu{|dF#eVtd_ zJk5eOlq6fw+qM?AuF&no1yRTX4-Qx_#clbYB>Hy6r1x4&2sY_&vJ1G$H*MXFgTfZT zxj2zy(WiJfLh6Kk?wwpNg`6xo^YrEE8Jzt{H^B9VM2~(Kgpr4T2KAcmBQ{6DTS!Zc ziZ$rduT`nJn=}pz>%!Oo1%OC65y`}4Qup!;IM8u0jp1t>Z3Mt^gBL0Zi2{#{Pm75k z&wI_%$MZL64CmQ>^}aqa}hzDEr zU$)dkhRc#JSvMCUSj%p@jFK>Zax|G>p>6x7TiG}uiCCz?KgfcY6v(5a5TX{D8O)RE4^GaFLm=tR>UbbPwx)KN<`mg>A$CgK`HL1 zl1MFE4nS*N$HFY=(P6=H3eV64rJ$_; zCWq2s!BofbmPHS0>04K3h9Q7??9&F{kV-W$O3r@Z%&~0& z1Q#xTloF*yB|(;M3-H5WwvhCJy{EI_sYO(#V}g=lsK6R~qXPhaIG%dIx?=F{8Zpr(9THbZJXEa0f#MY`u zJ7Gg_M{@ZLrVnok&6sf6RynXdXBTS}5cp?E}ZDT?w4z$q}r zf1bkT{S=y>krWj%A|5$W#Uw45PZPOJlo-{Vj7BY2*Q^fJQ~#B#!F|>qGAfy~usAx8 z%jqB=reIERHMiZ+KzBTN2GwK(6V%nwK^2{PG16t)9BwbdNM>FIW9>p7rj_$(ZWrCM zUilJB7Hw7$;0^cp^~|`^mzoD7+6%al2zZGAtIOnOGRD)cpM&CC0hQmIg0hE7Cyd%B zJrjioHts``ES})QJP)+0M)%2@ba(wp;9SGKI=16qlu0quo`6Se;=}e|Fn8N!M`29C zeoZubQSu##&SVGfB){05*sp{3;;2EGnz~-W>VMwfo@CjzzE!z*nF0hb%Jr;DGlIho z;=8W>KQ5!%rD!0U|NLf0$e+2q&kFJQ^PjgGv0cVHKm*a}PrUvfytOkbj0gfX-zmj$ zw=%0`CxD83aRV*#{x$l&L~w{)kA>vV@pJOS_|J3(Sm}S-xZD-pUg+-8`D}&2*tW-1 z{5`H^T>Xt4t#Z;%wKH=cvg=yQy&{%Ta$VBK;+@hZsYj|I056; zeVI+8ev97nAo^~}sQ=beH{oq*qLEq<+`$o(dQgdjRdT$I0Zl1~gEJ&i_rxMOo5kN1 zp|6Id0e&*3XLN~mIIp~+RQ~-D26_+gJ)}it!#2+XJGSX!_ z2$DS~sq8d)`v~--Ytldolye&9FVDw2KuM>ak&}>+xK(=w-Q7g|F+z0VN3b2Ee7mL1^lM66;$Lz4x9yb{DWqJp^3 zgfEdC=ze3I{xky(hHqR9K;2Za(O4f${CaW4kLaRVQn)QpSS>>KX5 zP5e}g$+hu7q^KYpgFX^8>Ig$+-W&}62~Gg>uZ^-7Gvb8_g%*~Ogu36r=Xy*+fv&r^ z&TNrnca8r3q_?Njt}Lye%2d^+E`)WUL((GdO&8uphH;a8*YOWr?N}{`?w3`OI3c#p zRr$wMi1xsGA;z1ZS5<2&+%@Q|yfe`i5G^Yx`hYU3(fl2Uv_N=dnduzI5=dHT#PRO>Hmgan=+snUZr2DqS zBtL}jE;8P``{j^Dp4a_RW9H@O{vgleq+@&T$|cYLee4f~eyfY4+eB(c4gYIwMTMp6 zlHb?L&mA56&TWc!H}h|%Z+pL=pSI_9>#0PgTzi>hu?p`TEP=yk1JGS*ibqL-&EXvm zyEXlLB|skdZ~G?XWoYeU&sbDr1w1SfBIZKFCCxOCeEKYoU56@;z&v5{V_!`22+ckY zQ!sG}sO5mkQ}OG{4FOVUk#)msyjhjX(Z)g>?A%B0w3B$*^7Z1demOvnlE{&X_Qsb#_kWHn1IW16pWW5ha>|L!O6)aiU2m=zm_3%R z)6j{*#l@B#G5477-+-oX{+}a;Etb!>ifi|kXJdp(IyKH)E`|(%0s4g|DSkXE$KSWQV2nA^9iM`DIg0B zs96mOLb}TP)SfmT0NRZIGV$@C!Q~&CQCL+!AxBd^P~~L}oWI-rO##!@djUD?wlOTD ztJpe?E!P~$wiPdE+?A;c-8t)wfhUkl7>MRz%p8 z6{(kH6!bGn;8~f#k{wS ztZ^B-<27OwIw*DRsMY-sqTtw$6~>S zUsAz8a|GjZ&kowHWR=GTVrQ5TWfMaybn#fQ7u)#5Lcw*mBM;aVNOh{baG!j6YJdwT znJVgPSfHt#+TZR$6$@n7H?cNwV46lDh0ANit<573M6SZc*K?Aj1jr-*K`_Nz3K#PGV?io)T@0C&Mr}AuWWSO+y3G{R2j|cZvfifUp#e zA+TjQI2&Xm3nq#bSR$b;daP}@0$vKDav-8ira@TFOL5gwNbXl{aYW@ii~-AZL!r41 z%c==<;)FgG#wegVw~O8s@hotmVT9ZcUNg`_3rXh^-OrK+ESPO_3xsoWQZHvSwr*r6 z+@y#Pb{@nMK8UCHT1%MgDjMo9AmHi8Dl$qM{;{`Mn2%}N1Nf@A*Bv<8WN`LEb>Ff* zDv1$=y!%01AOx8Fb%vKt9zwDXl?sHfo&b75i{cDW*jp>4p#-VP7*`W)%-Ota2#H7D zVj41S7o%ulmFLlqfM~c^UwK7kz!J}+m$kb}jv9rEhL+YL zl+i@!0e%}36YjFm)bR?#ZObi3XT?c|fefL&BA(@m{Uh3>Ms;ZyD$8&OTOZo29krZx z(4-eeb593M72g}=5*QHG_Y3Efa0y~Utv=|&fI}AYd#^-@S!(Fv#5_>i`{ibTb(c1a zoR^bNlRm{O2ZFe2%_cTB?(-JWchzxIuFQKumi=n8&f3rK`sQa+K8MrW&BUmkO$iqp zze-gq?_(2l$6+OhA)BMyeMV1qf$#fH-uJPHpo_KRcC6=hwW-5>`~BLzRMS7={1jy{ zI}Z%MCSLiv?gdySFxwG>eV~1^0ba(;JMC7ERfSha)ch+!a`3;4?Uje zXEFn@fwpQBhD+0mRJC`5ErF>&X*!BK$>sf?6~^tD*z#G4T?0ehDTtoc9KUy(P*eWQ zr9qeI4{?NCxD65k-lY7?)S+*3VKXj2+)5JIEV?4DICu?V!ZT}W=XO1r(O~bpfh$H` zRI6yi)rRM2-o2lvRD_Dumi--umqeF%-M4 zIfD?(9r;@G$MQXgb~^!ycw68{`P%I;{o4Z2-xoHhQ9V_-SZ@H1=UemsjNu58C)5A> z?^{{7gnV`hg`D#ch`S-%$C7^m!VRykxAgmTN6}w@)TpYXt$_i*mHNfAYUQJ4(B|qW z$qsoV^1II`QJfb6*(9Zh1-@WrS((Ddt$R{EYkD_~nJOVm?!R#-9gWHI>gN4CvtTJ( z-@?6c^q4}B1w$%)xXi}x%6Fi8=9z@9YF)pT7gep0nuFmVK{P55_rsU0g8++mt4A}Udzv>g8uJlQNk zoO_@zomSk4W-Mnt) z25+8u>aiofcCidUK{{Fd3-x+DJY$u5)e*RcWcCuh?Ie}^Q#Ut$FZIy>7JYH?S22C5 zlH^~@nq8%hjAv%7%`I!pWj2kFI~(tJpz>x08U6I@T+(;$hWhX8&&wgD5@LzUTsr@| z$VkFMXV}N7mKK^LCG1;@`ji_ZqM2skf3Jsc4@PUp(rg{pyb7eRG+w~)43g`Ss3X=* zIE;;`cT)*fG3|%s4RVW-tMHn#V;yuWKzgIJ`e83gz`?RT6okVbEQ)DA!A`nB5ezB>J-6_L|E_DgXE{(!~8~@7D^aM@PO7vbQQ`HdL>$SbYMy&YHT+| zBbz0vpz?LbLosDs!MFU^Fk;IwJ@&peC>j8bjd&m4H*G}r1mlE(LC4msjd;#+)85m* zJM9gv9kO2%^l+Y3JWAYNzUFF>%l;iOJeJU^kRWu7TqpF^+H)74meM0e&%j_ACxP%L zGm8Jqijs_Jk);z?dk#`i0GT?B8VxilEEl#iuvr#(Z&ZmD2n_Q(5ZOubXvpudZzJ8! zYXQG&ENb)I_`91Zk{fEfzyn>EEZ~Zj73yhg1wm)^k@FWfaJ~ zJeM(;N!8xaDAxojt`(jk)-=8(lb9*h08b1KssU?lMw%qLL#x#L$?7DqOLizDwKqW? z#0hEse1VK0hS@@&nhQNHf5)HhE)?w+u`UO{XXU(iQpT(Oqcx|t`J0!Qs~vSbrp&6+ zjTg7;&9?e(HT&k8j_Y=cJV3Ggwk}{reO@<0fY|?f+4f-H@iBEWlef|3O_6yNt>1bg zOzkt=O0=r7G5gmC_XiXb>UGmZ#{+&2=i>e z`G&lMNY1)$^)q@2876OHx;|~@8K95a_8~_fNEQWY6-M%SRTHRZOP`vTY*RyOF=n$2 zvIT*E=~F9|Fwqq0&F1kh+_Knl+^AXthyc)Q1@JuXHyaPqz_=tkj{HquW3@T`X>iLV z&+Zd1!;XcC`cLP)kI&nuEp6$Ww{L=Nfbx&x)=-FYadKJDRTSpb)h?%~vYE^^SpWkI zLl*?d>>4IOB7?Sn(@kyg_cena^gcjV1MoSoSOCRQBi-}G@U%8zb2-X-um9z}cg1e< z|IqUj!0fx5`J-&kZkLXr-L_WxApQ6)ewt`Jz$8CSe?;(h<}mnJ^ZlO`qISp48#S}6 zE4^%4+@NBjfwyI&nuW_p-U-W>B?~z=+ogYXp6i}kjI@zysTk@TG+#zb)v>ArghUwT z%Z-IvB77Q!Q}hlAZ4pe5gjP|osL(}ljX>4NPXw1s=`=xTO#xwWMuqOh)0Nq6zYSAi{yps1GqXa15V{3;B-t z3K9_L_ef07MuuJm!X~o+a?m}EPE1VQ_V-oV$rg99nG`B%bXZq1SKN{Mh|{Mk^7&Yl zA-o0nxKEfg;-Y)Q?|9TXfA<1G-yV~evQp%6Rmu4zK0KaP!gJ8)uA*#r}-i+$ymxVkffSMM+LQ8PEmK_i>r1^U>4b+v+=^3N@U2^E4A$ahe!vw)($O@*f-N;xw)vsR%rHeQDl z4QmqdXA;_p@C1ntVRu+;jKZAO<#9U9I64|0KX1Qzu9=*Pjf-_(uaYX6zjzW86E7_t z$+W)m>hhc_0F5>CwzYj4zNqovA1gz^<8j#Q2bZu@U8>Aw~OGfTw(AkVdeWBb$(Oj^75xD z!;NKSJ_zsJ4pIZd@27lH9k4-bZ{AmVX~Dr~Bl=!sUk?o=kr<0TG`=}-zwvb1ak59| z_c&gwzF~>6S^iEHr%bEyolc1U2`M2jzzF{BEMCV$Z3RB_LaHcs+8ws(czqd~mip3t zF|FNvy>(ndw3&y>?PV9teYp8&FNKq`BbV-<_iiuI1Y1EV>fB0wj1C_v-xBdq)ZOvdXSc45D58+B2JmOsoDC5>sUYjC zfRp*pJ_~L`Ug^uF*A3$LDZr+*-3)AatH?5~u#p&#z#}hMh|{_GfrAf*DUrl0wq$9#dWUe;@ zca!mZD8+Dw&ww5vntj@Nu&r1fU1pa)u*eY2fbM)&-8=OG)_YxL7u*-1*Fueed7Xn5Ehyzd_N{2`%VHF&z zO$b*>h&E+@csV)pGj#V47nPQLu8*3bMLR8~H!%aT+C!no_q;Eg&ew-+BR$`zffe{a zUkiENtq$Ld^My)PpGzyhZW+#d@pbJF4m{S8rHOQHN7tHC@?Tm z-R+9R$O4d7ST`&Sld% zTRlIs{_m^$dK~WW_m`Ii!r|9%>!8|YW&Cer^7y{*W6_xOx!kVzx4*+_!Q+Cqc-)QU z#*7$)h;S{3OJNWoD1azszz}nROwVTHy)E!qX&pu<@}-$WPp4|3@vxPtuNq+ux2!{h zcuU(bBU;2@wTnn!jGK)?I+%D9Y9v@(;)x^w8ZJXga+8%NSJ5)?{Cw7BRM2@NUZ#lr zHk(K`0~bC#xsHurQdVd8-`CZIt{p%KWHihoIeKBiK6)_g^5J(!K#z=yYMxI|QGCTxI1U?o$*Yj`rm1EFXf~OO~MaiV^ zEW8`lI`h09pK-OG*#mKrE}>6amohxo87|Dff3jlVs-o=>>MI??G#^O0Js4w~5f`b` zS`zh~GG0jqXc1eiPNsw>P$dR*A7KN*^;b7Ri9tOu)GCCo;S3v?B%Ig-pey5+fx)I` zTVvXF(S$MlK^umfb`;Uc*1<4=OT3Oo|=F}!(PM~?Q^1}T|2BXE37Nb#`A z@PX}9EJ++h1RP=DH023diW4h^a|rX3HZuP$G<&26Eh(sqm0-(3-Q0xMs)nX(TMQ?c zPkaSq$$hi>Edx>(3X8c&Bywvp``U!}Ps_O^>eHA7Z6GOU%Ae@jHIpOg0v|edEXAV9 zqI2-MMNe>*ph0t%Wltd5$`ETq^1x75S;fe}Gy^vfd)(~2GvZ|kg^l`7jTTv1d5u)pE!P#>oo?5Y=PKoe1t=!ng;Me|+KB{oUmuf3E;eQ+rJPK>thM?Q z1xh))4abS9mh`2$`_*Ej6tm`QzXe@(m8hmFQ}2<{(Qj@071pVv$&_EV-dYT^?cLWR z3b_U_30bqT68wsPqky{RV$SQw3l_rH9cJ5YzHg)5jWh+X1mRyzAl9Zs{jd&d4XMjo zDGPCh%p~U3jQ~N3r?6#gWN+|8E`--_;lTKK*@}qNmN{ z^z?bD&e_S}M!@5E#3cARd~-IZ*Zt0$#D8c2_@Vu2VX@8!@a%TH0k?!CP8C^Cd%oFj zgWA8BvPf!6p3%IR0*0S6)JzEfw^ghFaR;y+HPG(2C6WFiKe90=_HAx< z|4TN~8-S0w_9y*F>yZx*{KH?%{avLWLoSvA;GAa>{}V%}zCe!V?3}}rwt829=)ZR} z`7;ao$X%_!*{wb2YWb1+nPY#|{V_H1(b8qdR)E!@zm!x*>Fe&BH6$$p9AD_4(DoX1 zXqb|J9FhoB0Axeq z!*CryKAVy+?1GUQB&$m#IZT#WjuRAW9q6BaF@veP1z)5#TiS+X0-SXp>o(~uyeNnV zj=f1VU{mZF6Yr4$s);$ohhoL3L`UzbiPzptibjjd3E(}>Zo&Ke-3_!l=IAp`WtklW z@{QQf&_W7{_ZxOpyeWZ@kP@l1M6owV{NyAnkLyt`EaTYKgqLl?@*tVo)g{Cv#lFWO4ZGJ;Vzsx!8~ZzGGE zu2{^gCwp$h{XeVOH-Vw--8bz0cftku@;+bvKkj;d5CPce7%IhhlT^o8?(gk3=aY^d zr-|tt)D#rYOUmH)wv@9PzQL)P7X-ZTzNO`7NP<|e?B*~(T8AqZiWdLR zhRrcpZ#g|KhrRJ@Z-S_}Ji0udGJb!h%9tKHB1k!1G;_?w3H18O5nMk`W9Pt1V<#kf|xt z)5o2PtB5+8#c#eeY&)mTl5NjGR8>LKo@1HM5$6rn%6RuGNcIug+& z(PL7)TJo*QbrOyTeM_Mn>M|_3_VQy zA}Ljv6ZB%{lyPhPI{Hz}21Jn-duUY?vdBuHY#}a{(B`z|ZMESg(u4dZs1-O3;|nh= zw$&C-hjb|iVtCP#w1YV5W*!^(4!AiX9NrcuumoASq(m&-5$r>?kf5q$A>BJmKDN14 zRaK^S4ARcTnC z$U{9mf`SOb?8?U>D$bNTk#r!zdE`tfJqzbt#rJQANk19Kn%K#i=$_&fvK85b8!QLA zvOoV86b^s&BnX4{4X@@0#IM9|Fwhm>Q`_?o2{r<}I2L!A8JU#m1Vjy>A87qb8bph91wU%Z7p^~x#*m=ZrTIRpXz3D|5yFb z|Cl8Yx7%rJZ-9N%g`><-lC_BPV%AQQ{?T{4+O8n>#JL9(K%lJ#aQ+_(c^G)_$fF$t4M1dV1=`MqMC+YAk$oJPT65i^H%%p__zE>{4b`P0Af7`Fz5B6;bRI}KMu`Pe(tH*%n^^O?4kU> z%jS&?|DV|#2Bb65oMo)hS&D7we)pGP(r3R}?Y#6j{`h~C{T$|On0rNI5^%q@1Fq5A zTa7(ZZ?p=FtB9%7fem<=Kc)WIxHb!t^_8a_-P^ff*ygoFap#a_ww^s+TQWEdaL{Xt z5<)A32lt0%MG%rCC`g-8tUmImm}^?OGC9V-S4Q=-s(oT{!Qx_ z=6t;$`qK6DWmfT-@$+5LfH^%aBd^=-VmZ@o8JO^Mum7EO!okb1+G9^p*rHkEfBM1i zbNN=J-*tm409NB}*fnSWj2#R{bhm%@=cKg(b3tvz%Dt({Yuo0;H~tkl8M3I@5SpZV zVPZf+ze<_EQZMs>UWfci?d?)@ukGOQ6!vh<`0@4OQ&~KeB5$GR-F2_Ryp3>_rbI@3 zx=SYn{+KtCXJ>jH$8g{HwY@J306jozbFRy*>!ec@%%-&VR$6UG4ZmgRL@&gi1Dymf z3g?YD4!fcEZm6A?<@x)~ROe`ymKnO<{=!>Z^r(8{klfVaL(01tTe*^@<%AL%dBB-^ z_UfuS>sOH^*uypYf~JS&Na!D($}kX>temJE2lV5GjIKyfNsur&uYJy71|ua#<{j`@ zg3IRF_Gc1F?O7x1>7nd@60)uWiA0Me_YG$XW%m&bz0T~yOCap}O#Q1j7t2-<%~tA* z4hDDk+tLpf6#d*y0faEG>Zw}AaI`6)aY(C1S5}g=g+B=Z=LGuiWj%CeaVQ8bthe@E z&~#y(25=S9-`CBVPqfwxx|+)1;?jDjoxzv@WznGwLl>K_+$CTNyWdrXw!zhk5ffpC z4Arvv0`SMt1v39lKu;o$yzL?gHVTl4w9fKIH(V4F z-SRNX9y~JM=}r%+lIEjR*6(4@zJ7|4-32EwBZL)71uThumJTd;Z*2z0*B@u-!`65X zFiU_f3P%=$eZ^pI1fLLSIZE%N$DZ>*YUUlxfKKHlJ!31abJhyfPzrDKeivjYqLM;x z4?r315)cBSg$*e1W6TZ+kV0j3BpyijLHN(0DiTU&fx1zjNr^dgXPPG@4w^TlUVD>W zc3z0`K}R|qOf6jsaTZdQNVlp_TK&c$k+1FwYesJqI?2U`y|nqB4ajNsCNbiK7+d8Q zw-i}dK|uyL6leSPSk&}X+U%0m8+B0c^-NfWxh3EkSUbAs9V-rmk#PMey%TZDvipa> zvD7>0iDT8#6G01H8gfU5upfVQIu43;9^NC_sDGIXGlygB3%&GLwgh#9?lt#&X;x?! z{6E<|Xv+lrqa&l`dG2CELqp$&j(ELaCa0&bHyZ3MP4D)HG+077+DA-TryO?rm!WwR-~#`pI7qvdMT?|QP$R=b7c>-!BB6V}bp z(9zS>v1;2&q};Oyw{KGDT}b562<>FR^&k6ssObuXyN*UTphEn8|8__i5@GWztVN3_ zt0uO^W!~=BktOM)2tDh#R&BM@Y@KPd%V53$77^r{l&WMLVK_6)iZ-TP?q;3s#o^$q z^k%_d6 zm9w?~Enwca?G4agKYKnQ|A(E!Ww{~X4g1Fq9Y`0jy|YIyyl zDr`q~m=(KT5r4kL^?I@tWYDliJ2O$l@SD#j^0NX!%jxdPVD6W_M(Sl7%1gjFl}iL+ zrT!k^XlBRRRsnOaU(p-l{su%8zyo6fM*(x=_B3$TMi__945&lo8VbDr>wzL>n-uZ@ z^{Mi-4*tyzCc#^(>p>V>2-7ZOD!gb4tO0juG@|2Fy(@zB?tyqu2{G|XC?`KYU#A70 z22?NIR*c@fdRglRbef4r0RFmLZ1F>8-#4X0$rGwBs2c3$`vIbJ`l4F{Dhut1_VmDZ z{?8d)cvg3x6%aWWjswZDOgZJE{4d#7Qz<}iOHY46g)NxwBo2>r|9Q)+YwN=EL@uY9 z&f3cBy?;m=65;Z~yKfo4fn8))=+{| zX_;j)@fMom)#Z2*>r#I_MVG|k8_E)b+8{$LnM=$`GQs(LtWM(Ql9ReZ&QcgAIL;h~rUxo}Fba+2BAS&t+pRA)FJ zYlqI++#<$p^MZ7!QwVFLMJ7d}6HazI;wvE$lAXs@o)TbDLRwHjPMJyR37r)IgC@if z9pNlVMHLFxDNLr@IPQd=%s`V6p12{rI$(P1P-deMG%B|V8po+ffC67OP+&Zb$Q(i0 z{P(ODVTaZxff?N(E)vLDJ2ASF@@X}A!Sgz@w8E`^c$k~Bx>&_kbWilz#06=@GV`TC z7j+iQI53voSK3fYvor$|vb>uW>-%aV-|KY0uz~{2Tr#Kl1T4lB2^0gB-{{!9u%BFs zWWs?k#cG#Br-n-NcN0XPS^bcdP@@RHYxw7rQlOfrr(q#sj(&?w@9z++!l7T=%PDB(`)~w>pN$;4g`$mClck)(3d^m-l5&fJe*`l3_8|3v z@G!DD#thCuvp)D=X<+ujdWt?Ad{JHHFIV017Ye*N>xtymhw1gru(x~{Smy0RSlZb2P zFjbIx2^x_g6;=)cz~tjNF{{yC_lZiW*ejjUQ`C}mkc}u1RQno=%S+L4h^*vbGRwww zu&ldkk@^am|^)>jzRT4a01>Ql9T_(R=6Nd4$0Ca(2&}KAY41VQ(u* zfIhc}k(a4I{AttPRE_{M*(I$j1v8iL{!%y|{|*FEt6ixYXgqPxKbHI5_QaUr6l)c+9nA@WJSIeFlN1jJux-<&KnbJ=b?F6mwQXSZg~unt!3IXRT-O;J{ad(!k>e@9^Y4|j7nwiNwfwh# zO?b=gfP&2pj5aqgx~ey!kc8D5NZ+j_p~G`hl%mc7 zo7&9NoM(5FTdNK*-Y(gLTa*CVRT6s``-ots8k@jUZtU z58}vpMIZt~9jP}|Ekoi!88Qn?Nsit6f_ZQfXG)*4<$^94iXh5@s8HP$ObI{0r|@jT zCUTq&Zwy9idPz^0&;s;y8^oKEUTgcz1Yj7i0KHedWq}ieW@ac_ty;$G!}EeN;VTMC zkyVGu$5+g&M}>QFH_k#gaU@uWkM%&@sSZ>M4~dg zAYf@uk?e>ogy`5vMUkXRr7|`;%C^-B!*aPgvt`EfLs``kieqDA-F7?h+*@wBZrir$ zQn?)X@s8~~Y7{;fARZ@k9&i4X3YQeW7)xA*S5)t~)|$wPZC z|AU{Z-FRU3=YOjFp?C5(9a#DBd(`*5IeW_;!EM(kHy#T1Zfi}~Ttjr%x_S-`m5~dx z+&y2<9y<#A)FqYWh?2D1qmt-PWP#{=0j-J%bi5pxK0za4Q>k&`v0bBb-ps*eJD zK{tFZK~zZ+y?p7?iPNWUz2&y?@o}_vkCVCtQk<0HIjM7s4R+^Awq8#zIdrVATGUSF ztY}VhE^__Lz#~$LTc@kuvCP@Xk1P6mFa=DhNys9lMTF&w&cf2mR!>&}XJ#=8( zRHbC9JGV~Q8ZD1Zmux%bf}PXjT;i1q!oK}`w@z$t%+33Pc+2g#*G9(dQgQpX?d3{s z%eHNXRjgD{AGUbS*vw>kbf#1rn_aB``v36xFM~)o*(igw<@PiLOf9!7V`iu+esG-*)#~-^{oJt-!cL2tW)%#vY;U#++z#vMTq^6G0NR z8q^MQiqFM7rVvi^b8~0TpTGXP8%9S)qd4hvQXeXol0vd5cF!>wgAHvolX;J?IkG{m zWYLTlM0bvJt}yN>@6W8AoLn042K$fIfsj(E^ymNiW5Zt|bARy1KDfypuOsdNG8=}x zcy_%sZ6|E?*G=yDUULWktH1O!x9wc+Zf-(xRc=CY>_TO|$?AFjvh=}s8Gp+*FuFX) z|IUBV>J4^*Bh~b`eI!HuQ5o+! zZkPm$A}x4V3UU!-k5GxS5P~UTioDHco97Y40l*oR+&Jr?ApQ{s{iNq4{ZXyv#4dYu zmqaRCRgFejEKwC{h}QWp$e zmn<$N320()Le7a?OR)`6jo2zIuRJE*>vY4N{vNSqY7VOlLHQ;JH z=phN%p(qD!W%`TZ+C&r<=M@DKpwr-7 zY5TTq<74C1T20Y~>0O0FctL4BT~Kpf=zj4lkKVFf+P3HL=$3sm+xKP5D`wrB*}ZFI z$0&sJ=;$~&dUR|QG@zXughPoLv*o%dSU<#S)0x9!KNit&y6xl6Wid%Q0Oj9)5XgsP2 zv1$}~!S-C1)C&uX$4?%+{nk6m`@LVtI#VPCD zvQh=?x6}4tt050LJxEeCFa8AC4!FR7pjt*TgTAblvv1s6N!e`y@%FpxvLKa+ZFQGI zwa`L)=bXtK)AF=R*bE(jz(DnpAaOXYETCKa>(t!E(1p$KW+fjgKp+7dd>*_sC`>ss zmK(gr2yQSlkPVp&j78ZLn-JKA*K(4ooQ3!KnJY`|8FjE}M1xk;7%@iBn*bc3)(g)b z86O$3%u*r*oB;SzEWs{FwyCHEUa`)f7MEtv@3>Xnx637QnniI2|B!#;R7340ix7zhk5mHpHe2w#+nov0m?o+6UI=L?*mC;%n2x~GX1P$ zD#{Eh_T5VoZ*s@$hC8aYToB5WRlaqBtUhfkubw-`P;JPIVx@@2;&_CSwz$&z)BpV6 ze*HK9_j;o}XbD!}R!*Ee`Imq7#m{~E%f1^PzVVQgJBU-6s7W0E;UE3+@NF|QGw*oU zT{+w2cgP)bK{R3#N3v&`ve$CsOqEqrZyk~OSkY~?jqfIam*wS^GiOiTc>PVKVhL@R zMv(}xRdq;grF({$Bg({`TNZ8<8@#A-+6bxc;Y!zW#ze*&>^9E;o$?&o ztOM?db3fwa2k(El)9&K+dcFScAALL0W0N~xFWe!((CO;(xix#t1Sq3ZHo4<_%pLqE zKK_fv(alXL2E=pqZ9=hsm-yrhBdAZOM^Bx6;m(^2e`_`{I)94)zyCxJ0)~%!cO}2| z^O0qOZZtRw z#L0`5VfUb;h9#LuyTqrhdZ#*JJG3kFye`U1y+%9iIEjl(E6Wv)XVdpUYCOjm#S-hq zK|zZ3`;H2FlG$xvlq5;po%ZQVR}xX7q9Af%!bP%xYOxV2pv0Bs)nd^kBuVqYkHQO` ztm}IuTMJu$zwHW^!iH7zB+n2GxI8Ff95P&wRxgE@d0sJ$UBuqPd8>jm|M&%G>)z{C zyX5&DbHE6Fafw!hlPwp5 zENXIX$)Gf*u23|}6MTP;tRLf(mTXBY>;Ca$&)<6MZIcsYfTcml<(8^cN!PA_J-2p5 zh3gU=i64xg_@Q{sZDi~aXKm%wk&LLYnrsdvvJ@2{oI|?(wAqaiT}CTvt@z8pb0`zU z39LerL2wI2g&7sr-Gr3oYDuo({q3;s#C_NgH})d52i_?Vkbgh&kg0wYS)xvPE=og? zX%gpbD^()&;+_*ah(lRW*g94`XXr{0CH;Po38|BGAfVe}LlfD&MkwH3=)`Ga$~qeN zN^cF}j6boKVneJF6Rh}ofLgd5O<-7ONC>dpjZ-<%c$x=fm%FJ-x+7fSpnl|)cU%BMxiiny4QbGH$oc}j-P z7O+TsH+7-M(7YMRG*Mem)`P*b;W*|-dY~|fVi$rh*a?9bcpy6KWu#pWw^rtqavHC# ziF1ou;PdaeEt(L+^7?U-r)_J@A#5Ps608dUidi|U>6*f^bq0uB%G;-TR6!dbPKbjX zz<1X5gCrf;27Dio+6GgwS2unhd}oXJ(uCwPen~mD=`-G-a3)(e2-nY^-)5^y!%&Xs zRdgTQ-I#D;oE|-Pe*f;h9BiN%;$1kDJa{~u5!&1HKtdj#mw83BSY}IBQ7%D~xU`8=Ym!prtG(EfB+3i8)&SgC zQRD#X2WXP>y0EVBof2lRy1~fBzrw!m!SNt#o_6ryqZ&d1-arm`23`DUvAu z-+%Cj!@o|9jlS;#Zy#{S@RtQQp7=nNZfw|sqN*ADNd@K`c zH=)2~v5&oq5{DbNGTF^54iNz%$WDoBNQ&5PJL|LSX`b0tvm`DRx9%;BRRI1#W>5n@ z1cY{B6H*8P0jdihM6iiGBMbBzz8B>(G8FjCi)>n=MCHMW*cUtoj>EKg8i8VDfi2)5 z@)RBbe+EskKxSRYG6fen81a^1LhuWK3xAq}sQ2BHDm1uNi3qC5mP-(cbY)?wHd;bD zr(7fu#vMpE_HkZ<^TA0W!er=gh^ZGLQ4y4qT-K=-_qiyH22P^hIC(F0*?f35 zoshi^XE%8Zspt~d=55g8@E}Q=Ic{hg8k=d#7E$upTBlAV6iX9DV+6`k6p`}7#Y9kK zRYt{nJr8t^Fu2%Zfvn}e6yy=7PE41f$7O<#d^7%0Kzu35p)d->MlR++MQg^DOLGe= zhl?#CSfvnunv_etGCq=bobc&Wf}!$74RA9^3?Yi6R;Sl#`%urL)k>+TQi}FT#KGh2 zVH&$pV9JVEZXLR*_aonDmBika6~rW(f@`+v6G!vUekHwpzAGl}G2x`56t66`<}O|t zt&Ia$qnJPb{Ao+GWm(Q?Hernh{kY2&3W~r{A(!o_IsetiPQLNh{adGMnj)*jN9Sg7 z-xfWg_d;4a=*!z69S|X+D8KN+F-1|#nTd=g2jYS)h6=3(g^j!x(9k>-BkvQo%zu<5 zreINus4GTn{$-5*)kJ+TSfwz~6^CX@64gWK#(4yS z8#2iMP*Fii9yJPLP7vGQ{^K|c+5yd?sgfiRL(xif5;22H3l!KI_p&}zT-Z!$#+M6b zMq^N|Dj4XGC{57R6{Qvt&q;j*Dh`EX4#$bvf9wbLk9`}6bp{d)=ZVP6)hI2mpONEk zt0Nvg%Wiz!K7M-2Scx^eZO?QBnm{*l1Qx=i8*5`{=qoM<%R{-s@JO=F%NDox41q~g zs<=#?oL$r4bS!*W6yX<~bR($%Zo^6r3wsbO>(E>haz)|^G)}~^6@>-#IRQIoNyH{B zdkK43%#v(SwQvNfrc=- z8MnpUA`(aA(*zhoJ82&FqHZf}bfR{{uh)asX2>>RtteGfGK-2`Q7e)t!ch;;4yywY zmZ_?91(jG*YN#r+AK$fvp|i;y{|j0p z&o8Zde*E!Id}{dm{FQ}Y{+S#EFln_(`jK(AkPfBLlETCr%&N6#^h)=SXmj^ zgKR~34!MIzLn8!pJ|Jwh9blKio4{=$;~%*9;b9Gng~D6k{^m{YKzY4z2OkB{(?NT6 ztI#4)9-G|pz2y%6H-GIn5R~}Z+=K!bauseuQBW?taKVmZHUp3iET@HflUr}(Jx70? zZeaAleJCfk(&`|2*|}JhB2$sSaYkZWD&en?N$gH6 z_*NogOtTo!Lwj+visi+r=I*(2Vbmp`fw!uwk-geYp zc2)tKqe=z3+Ek?L4wc^frq;oU<-VJ)G&N*hnv(ey`Jtowrym}>G#AZZzVfwuK7Pl| zd)1^fztaAr|M;J_Z{NLR%Wf$L`^lzma0yHe&5Y?ln{tJ6xiT&Y;@L}cr9wdkQD>3B zB@*Y#)U6lAq9F(#@iWEIZ~K?Gz*)PZ)`OehiPoe zdN=GrF6Las^g7TmM_*fz6G@(QqmG|=y{HGML`a0}$v(F812R4iIlwF_sS{>Z$a2o61XV*z~^iZE3<0DJQyhY0&k3TSMAP~ zSShSlwya0yktf;eb=&vy<%4QnwH%5pK`J2kMT43K-MGDu3rMqs=Xmfkj1S9bsYnGm zUq43JzP$=8GDDg{bK#9ryKn&Lc>r40)$LS}1BBl^HNFgEL z7}Z0d%0!hp4UOn4Uq3Z7vwiE-c5%QRx~79WREg&LM5vB?e6d&>Mc7`NTPWBz(V?9rN_CUDd;_y710ZE^?7>xDboO0gv<9fvDz+0rpfo80j|<_`YXe&d&L4xD>) z6ABar%~iO8(cO1CL8z`ab=HKhENEzhtxNy9*}&+F_mcngiH#`y!4JFdxtl#*jJ)04 zz^J#mfzj)21Eb3;;WN*&`@+{;?@#S)C9ls-C`u-Ky!X|Vmu^1|_?5*ULioj3GPnrU z&?KN>e=+YZa-+8ndYg8Vjy`x40?025lCCMHuD83+>gt*xN{TF{P&+v&kd4bBy+3+Wcc>~dj9ngdfP-04PuoC;=%ZdBFcIXB^6}TutQ;V*jT%2F- z=OSjxe?!JpN4X)Dzzp!gUxXvxhXXeM6!5dFsMkrL8&F4>^ICazYH%(5={MjG> zmwUeaz}FwStZ0YseKh+wf4K1JF9@Idy!Gq}^ZcciXP@~z$4h(n?*E(?GW{Pl+){lXW%^j|;r z@twQ(T)sT}iBEj$iKm|a;yqs+85^&Sl%vG)BZvwfCIPC$66_w5C9*`?<7wEB`t7jk zr9RCCQvwvQmIMi+L-BsdYd7&cNc1#u*n@lQ)_j;G5NAo2w}WmzpdJuVH)>b)3gb$W znTqZsg%W?#uu6FCK4y530zx%-&8|_MRI51N&cjBB9DR%#^~nQH zb%zsk><$7;6;`(lC}Hy(w?zi27^CM>q70xXARq;sRJDc3`zaV$~ zi+}NNXJ_XJb>aTvV}G!F?=Cm>Vd>?9a{VpW{+%EGq5Hq~kn1vm5yi>9Pn`MDkN(`` zmYrwLo;`Q&!thg~IjWk)xrL?eyZ4QZPo6pd!s$~N4j;O9Y;58a|MkB=^!10IdH(pV zHy&>HI=}bt{^Nx+XTEaJy*J%-%d^j&eDL8XIV$elvFqRe;h#P7#IdFm?mKkLpMUIA z(^E5wEdTz$`Q6*!aQhQaJofyt6OSG_vfgYRII#b*Cyv~E@BN?s%wKKYzGHrV;d7t= z!ZXi3ci#i|RYykAadGPTag69G>*a5JbP>&Uy`*uDD>VZ3-^0Pj5ar+h+elF7=88O?DAy>skdL3 zPtJI+(@iKQ3)_ZG^Igj~Z$Ay%mLCj0v+N@&Cq(EM;9-tnsGr~q+j4CJtqu8t=z^5T zcU;sU>5vnoU>TNeuB_EXQA$BuBxOB2unmBE#L}P{HcH9{u5-Kr=K@NvuXI{Xr{DEJ zq)RL7oo;V&X{FQcudT25+CG>wjN;Hub0Krxcx}C{>RRIaX|FyxHJ;^csYTYlISsso z@?zOSqh;-=#U5XmG=V*Z%tl0eQ3vLyESGSZ&|&Mr>Z-rij@yBob{g%OEmK5F6!bKU zE>`>mhl?5AkO=XMRFa9PLwydS@ulL2@#69tsv1U7wNjdyoM0m;l62XPwwinuZe`<* z-@f2Pq9~M01%xNtg4Rf*ELiiT`F^?+8`bB zMziNQzE)GWXj=`zpu8?t4-e|I*K1?rTmSBl zU$5}~p?y2|@7em)5w;rC{rBB_;NT%d(x*TB>36;RJ!ejye)Bcg{rJ!Q%)j})-@EgT zcWmFX_1bH%DU`}jKlN0xSZI6gpZKw#*tL7d!w)^Y=em7K9+;X!MHZ0ISpB368=L0R z?y^x*w2UAC`Y^5l^M2@9k_Bak#z)ohgC*G^#Bm1POQCml5n6>k-IHYK{s2F~OhU^- zsNoX@#jw}|5otv&*otY2TAHQZ(3J%ZYKoZP(GYlxW<<;&6oK{$CfhoCrk}01FZ{of&$_`&>1oM$o9JRbZeY@S12NkSNRf~<&?550l?5lD*nFc4=O&J;b@?p$tcxoK4~x~ZFTSz}3m?kQ#x4&2Zg zpLxaRu&A0QNVWw1jn6ZI%D{*4%h7T)0C|gA83}lMg6qxGG|Y`E=!0VmU*c^{ell!B zCQwiVn?eg;J~%8ZamE}Pf}Oz`-iG56>SR5e7FDV$kcb5M6TzoKVEq5s`wKwJ&hlIw zU+%K)_&Fn)%*2H#NsuHEf(3$m4JedCk>a#a3Kfc6N=pkA3KVxI1S=9FAt9bg#%GRi zU+(MwylbDCOhSO%-~Zlr=JtGEm)~_g=oeB3$%@lvXpLQX87)4q^xtkQCe?U>q1y^?Z~FPoie(O6~HP*@Vylbz&?g^Ja0eJNCT#&tB$sySLr3z0sPyd&lml zJpJi$!LKYWY}~l9VVjS<@M4*dPT012+p%*u-F(NHXPy1CpZ@f?<4@2{^V{F~_MCZf7%jRpZxpvDb zTeC7YRC$CQEAyQjZ~WCLhv%Jn#v{%-YaKg|7VHoiA@CD-=v$M)7#s9Xt59 zyyaCef%Q!&nn(O56yxLLtug+DQ}vDVJ;!O+JoB;9XMaGuODwH2Z~D`zBYy*ir5$OG=e~>)v>}-7lBmAnbZ$fd@Zea8uUljTSv|;(Y%L~N{kJ3#jCha-onn=FN@*|d2f3#U`j|3-eCA~VgbFh1nXE~W z5PTZZ$X>TA3F4)^W@f@(N#AqPO0fSrzj!X7$G-t5qSNK zG(WO8E>9@pDE~CT5WE0UE98fWLAqWZw~4v<{OpOB7_PG`PDR(JYC(|9a(UoNy`hw5 z&|~)|P`3@!yi0R=sn{#s`Z=eUi@UD>)(@^-?1l^b7cai}!mF;j>a4RKqA2PW-~R3s zE_>pR+wa;`AFpqizWNux*f6`{nyatA{mwgg?%c_9Jit^E#I0M884iYb-M0OV)6U8K zG7G8OE-h2f_d5$-0>dFo(W@p=cG#W0V9B!Jl`K`}MifTD0Bucd-Y2~c8H zZ!=ND(NjQ~aDoVDp`p4=+i^f;rmW2Tte0mR2Z^4flE`K}QDm43F2qd`uLwH+ zw-TRI$9v2%BK2gf9Xy!OB)BDKOG?F05!K0)#^GU z6g1?7Wo2+Zi9xkhAXuP&fWie^^wNPLm<5oI$6bmbP8fxfvPHaFZWU=v3QzeBArT;P|doE2olVKIOM!@*TmPLsUb zbsaY%K1?f8Fd`#~aRWI=7_OG%lo8j{unfR#pxc}r5pFPa1)jB9kTXS*vJtJ3!bWa3 zrj47jiQ1d@_d_9%DdQ8`lp&fCO})zNaUPVn{Jj3ecWmP>IXl&ait$E zFDzg1u=B6K{`%8SJ6V?1AOGk_kGka1d+y#bYq#u~iC_NeSJP87zr5l4yLa5Pckf<~ z<0dC3MM2oSd80dYc5L5y%893EL7B%eVqixWN9mQ{{n04TlBzxZsgGO7j-v%T>S8_X zmT8_CWA|yGb?o@B!4Ce-Z+=zPN+jpjH=#I+HlYyXr7!+?BFnib;h*so0abk@Z(#JJ zo0=c{0uAs(D0}^%6z82;b$qRFU_|t2eG`hKaswkjU_SgQx`Abs&v@qG=-q^3%$lKM zJctD4P>p+{jKg3Mm;@Rp3w8qLFd7QV6e~Be#$2gPpn#y@R63gVuokFs?dVpcl;ojc z5DSUJEKX8QtvYlLro%sv5{~$O=*Qvo8SA@}OYyq(~KBK`R7>htIjsjvEbdN)1u~ zro)RAbINQv4*SgosKL*(6$p2d)(-G{zZp z2|kWSk9pq^4CugyE#>j8=bYL*_jLEpJAO7hyUFu`OthqeB6W^CHnp`o9(y74jLQmJ z{PMfrasEu}sytabusn_1X0bEuUth@D?1l}Cd-q&)(Ib9!%Wa|CTR1R(^DTE=_T(pB zb?r61VJ~Fz8*jS#;SYbaT(3*O<0R#BUT!F5QpO#BfPx@6$9bX(DmWIElSR#sM#zhR9@3eR4F!lk7$*1}369og(#?+c(;y&QnPU=G_>Y`%HHqJ-8 z)8nXODRMSUJ%5!48Rh7O~bNOHg> zsBsQU#4m^}Rvh`h36CJQ8Y9vOj7ZMD_CuARh&T>#Ob4N7TFMv-o*|aej1{56p^~UqlH}Q% zpn@ga;h+#SM>FFhsxrdda=o=@Cy9qAK6xPOWRM61T+Ts4Oi1HWsdGf5#2C?umKvc; z;0`t3)>xHF&!v^OaqMs(XH57ES~Solm3oUP3amw}Ad6_a4NHpk4M2LlBrr5XH&jj00v7>;Q+bPzv~ZZGJ`=h{JS@vph}<$wVKW8V5^)hG zXx0_h3}XTJjdfiaGsi8UDnX1gSZDBi%5c65*cN< z8YUqt(@cIb@j1^8f-od_6adT#D7&ecwrU}Fn!dJ7?{7~jjZ7eOTE~w6IoR=nzyGVg zF+&%cIzC1>gX#H+(qDdQ?xd3-lXgLRaaj#I=-~9`l6Tq{+)N-we#-X_?l(e zhPe%MbF*8wZTs?9zWU_LFB=XAt?|~z&9m2Ef7AG6WBZ+VJ@la$7>2QX&+aqMc*y?! z`#YWJZ{PjUyY89veNU2aEF@)WW@dT*zy%jP^!7XNiu}Ry;?f;=+lC7A?hHNe707%zx;)- zjRtmgJH0=B`3sXWS;vl}0y{uj;?=Tl+OjDi-r~qQcKlaj2mi)5y>fjMilcE8iqhQp zweMw7%qepAf^)Ct1>uO@z$g{$xBbgBY#FzD`zwP-Um&b+U{t+_B73IyAXP#$tZ!h1 z6ONV*jDC83cFhfRzo$JRJ7FV#^ln07>NM@~!6X{}#G(0eLPTT&45TVW)wrnJ%AUKo zp?*qz7KiD0bpkCfSdkL^!H1E(_2ceRxM;~%U8|Y8J{UM@Uh29^CjUT*8a++g-CnEF zkQ8do!y6F^&m!QUH`LtH%4)OOy3@^Vk)PIS-x@?sxG>6&%X(2SVWWoD1P5Xn1Pe0_ z@;qf^NkLe~liXHc3hn_1QS=; zf?eb525-Q9JQ_ins)APGCj34CpNPUt#xo$csuZLVKu{O?r4W7skb%)W@agrYZs+UxsDj#v%g8gjO8z*cr7PlO~#C z3lT4k*))pVz3!Gxn-Hu<89;)~%`dHZK{7VGP3Cic`D(_TF7X7kV_C*YL_<1jVy~74 zbDBKUVs72f-MMH?&!!C}P6;BB6!@|-j!-~1#9zc#M2nnLK-2^(0zfT=iwHdAL8Dna z`J_#@-hTDVPC4t8{ZD^-^s*P44>@6JZhYS(&#PVf=o1=tVb`q5@s_B_#~-77=f_0f zSvGyu%TM!MciiGn+&VS4@z~bXhThPhZ0M()v}x0^CpUAcZd$`Uc)}AdgOcH7uG8tB ze(vcTrsig5ruCX>*a{WJvKzBn4(L(Q(gpaxY?U#&d{IQa!)a4SA+T(cr!ue3Y8${Y zbU|MmiNGgI{t8$GVG&p8ykQD;7yz73;^i524pxRo=%Nns36j|j`;eLl=8&vO84aT# z$|6zVTe=&y0|0WDiA z=``6n@qmE+$Zg1DHYgyQ_ltxLb3@mB>f>dyB14%a88n(rvkVwYrKnN`DD)jsr)snN zC-{;mh}n?UR4rykW86sa<6PJtJR9T(pf;n;sgmlK0SFgjtt<#Kt0AZcX#h_wP_G=@ zGHdXCL>oA%$O>>Cf!0hEs%h96uvlPa=%A9z3{jya?ea*2In&5jkRR%EL?!FIWO&xSMzeW3NQ)4zFDBvu<4M^U`(<<}bPl zzsJo0FPd15!2x3o+6=1Lm^3ATAh9AIyb^({>Ys=*8ls|$Dg`v+qKMUmn8-85Y}(B^ z0TJLC9=xBTUKRH!Kv!{z`~lL4k-3snOIuD+K$QOYyKfgQx^$a7>-6YL;=%7+6OIn*B*&7age;BJ0B&S@#9EFH|^YdFa zZ{TG%6QY*c&^6W2RkKnwZs`_Wj=1V%L(rQCxrIfN3{W5I*zrFHJHG#mpMLb;|6_E% zBnWRk8G9g@lSjzO=)14Gjt#SgvXjK{rlfqTeIA> ztjjNd(&kMYWJNps?6VbBX-paulVjtpvEjfuZrgDuo_G>;m}y#L&8DD8)8oz0e1UpI zR5f}1>y8gRziD#YHjhuwZfcCrLadF|wBxtU&22iqo{P3&I7RsAM_;mG!yG5Ez24xY zGftWrpPrhU)GR}{WCV80u5Web%w#zZg6CH@*2)G`g$`@kr~NP~=C+CKPw>$i8+3<>BHBqKBUD z9LXCP?epip^i8UEJnEu{-nhPj5vrm}Xk0`nhkt4dDu z1fd=k*p8QFo@Q8^l4%J*P*_S)qvbqS!XTnja6SE=NU8=El{kbA?`dt~Gg_20(5^7KiI~izQW&#_U#}m&?nm zL&wDji{wo}BfGz|h;X3|SoOD=i=t}1&8$CS_NEP zv3*zFGRIqMLJ_qFBig-Z9+7|J>=a7P)n=HmU-oxK>I{uk#9y*~ZZ%k53>F6Vgp)F<1Eq-ppraoUV(@T zg5hW0Zf7S#4=wG%^L5pzs}t1AON&jE4Y?tRIqVt?yAUJP#)!$u67Pl~%!4*mlEwC3 zSFb4rlh}%7h+38vY@+#t2vVcS`LUWcu2Y{1)T|qOh-PR-T`(X`fm4IXYp52y;DECz z@={Hr`4<4DZWL#3#2Z1Jle&Z7@xV%n)F6aN4*JZI?a`}nQ?qn6NdS4s+yfvBPRqFz z64zf%j0PA{T=f~yO0=pZSENlkrSTA0ILGigBvM%{`b#AV4H5~ernlrec#dTO@g;;u z&|=d4g&dcen}C|aoq%tc_$Eh~hA55I= z$6?hY1(8$!?8L)?ivct56@yUh6Te{O?oE=lkr53F!=>PXX6Ll2D5^qWd65Gt5RyErs+5qdaw}T$ zu!2I^iK?(F4ne2k-!y=RNo0CuL18BAw1#EsrmoVW)QW^7wq>s_bqxu6 zL4-|1in?T?~*rd45uq(lempWk{f(Sv#O<1#d*6&Pl+FQqt|2f#P zf8YM6KmECg!nWP9j2;v*gwAAj5lw2^94E(C*Dm1B?DGBsYG8P}NAUA8ATw{4o-uthSY zFbpSJ6US}E4d(=_)-a_>eBY6X(@BL7$7Lmn;?Bd>!!Wq{$Fk0V);%MK%==3J6EPqR23UQJ+?&c z#Tv?(<7$Q^L9@{a!gx4zHC3h;Y}KrITuyTwVuabQWm$EbER$Lnsk%Q}4TZwfiSbm+ z*#=RJ;I>lW4F1$6TK3+2B2gQOYB%b{c|44Vs-Vv3vl1`D^2f#+a5M-B$MZ!=0-?bp zN{%L$N`kz!xH>sD=H)KH0#FE>NAY!T_wCrWWh*YvOD2kA$Mt|)mTgZ> z(t5EtUgSk2&+@^3xr`Ihf+&L*BeywIK+h1-EQ?esm{Yb%s{wcCer0?juPKILHkJE~ zeE@WZQ9MZdh_t2#qXp4o0WKh5X&Em>d;Ma_CyM7q&=nkq2w8-g&$GGbM_?DU5+++n zaFoHGSHDwme3FaLy1YoEy#TkBc261HB6_=1Q4b=oNF>Q&{BPYD^}TdMBjU=Ho%#t8yhgyPUaY$wzi(6z)J>KZNaS0GSTFI%bf zpom-yAZUkuCwB89?XX)qfAwp|O<1+Ypry+4us%%RVFJv-siF%T`ti8P-3o&2gfFfcBp@-s(y8Yn@d5(XP zN5X9U4+ls8;x96a1QvVfATKhAa zy?giKE5Ks>*k)sc$)~dK)8ZK%mx+8C=K;s$K@h<{#Wn2ki@|{x{E0kp2s{4xMJdl7 zfJbzC1KGJ#2==Ff7Dj%Nh@#5`Ke#nWXp&8VZXAu9P}G$NyY^EMcq{;^Ncj?NL6rSaR6G>w%ec>HE~9UN z;ZlV%46r98cF4lx@HNN-V7x}t3Pb_KBJ(&_kT@B)!f`{_k|c9z{EH-tBJP+Z>7M-u za4St!YBntfkN=P;8mO7vh7ZLdUA3Y_W@xoc?{IQy5D^g&+#k4 z3J8`h)h(%pfHTS@xLT5C?l6)>N!L_73`N!iH7vq7k8~&ihNX^$`)2jGB#D*4+EIaS z0$SS7#0Zrbd3lzVx~9O(+NhU=l_jSJuLk6n+}%6(CQ)jdR+J_w?HwJW0ciWqgOgSO z*?wM4tK=1?f2Y7Jsbm1nD<@)5$?#+}TtjIFMa-SJRakZS9m~Qo6Rtoj2p(P`xF4Lp z3ac<^dVxicEOJTYJL$5YyEwt-YK)k39Q;v`sq8Y?0qmay1gMX{w^+X6&TMnr0T;0GOtEOg+mSJLtP<#sR5-FhM zF&lX)P82NAxk9!@1w?~K#`Jp44C83%d%4fpHC>P?_9G{F7Ew~L*pdhkY#9FKrG91q zB6KN`Er&b-`;HYpHB41Z**FB8QC}b`S3p$Ha+;nK(^_<;BJ(fxBgDb| zAp=BC;4=wN6Lq4`VG_e61YQ7iLO-{Zaa2pSE?$NYCT~MX@C;{5H5y&dvc8wthKUmZ zkjS&l5(kY~2Yvxrg*rjO+{_~p0y)8PYN^77k%y=R1fw!(ZRwV~thR5Dm3rHn@QN_u zGlAj#GH91MmbJ8h}QH{TYjCT^K8p}yEg!eHuDxld?P@T|8hjqDWO0-*R zwQ6DBl=YejT0mo51#?1Xb9HcATyTBP@=`I)f!(N9B+h^T`?pB3$ zV$Cw)hJ-apTvl=^$`1Jvq~UU~yb>4A-&ae*$(SqW-d%z4~WAzk2jg=$*g$z+axY<-`!! zv2C-^@%TM$e#4koVaI6J_9$7V_5C0IsN3z0zJB&|Ul_f6T1#Ac?jz5#Si8awxFiBR zI6bW$4D1l0P=Ou6e6f1-2RcX+x?-38U+}=)j z?RM6-#dA(7#~Nr!Mb=>0Q3Uy4@}iiQA#U$y{-QsRU#SBR4O|vAiqsrJsL9stdhN@f z`vxio&sttydG2#xKvN#pvE#_b4&<{;ithe_J!7D0*0JNi9y|Cqzwz}bG@7<@0(xh* zz6r&VzX`<^KTWT_fok$uPvTBJo}{>Vq;Ft^xJO@uNIv_ta%uPa21e_fP#oPG7=>wk z-SuSr;HwWki#d8Xp%|+@*w;@vv;im|%NJls)tVGk20ViWLmrLMD+D$xy?M@@L7{O3 z*=t8DOW_iJ0&;`*BSgEo*NZyCWLU8=8$@JuA1Dow8{4pO@?tfaVzt{H44k@ckB>Jr zRl`Z6Gm$EWMxV1XhM589WKQ+dKoJx~e$Nk@mb4hsNe!7u{GkK`$QbHHCoH4a_B!?I z9F}lXRhCsn0T-7fY0w>l)do-{WoDZ+8<*q3{~ka;AmLX4kR`4uhf!|9oxt!C@TTE# zVyrcyBk(deb-cuZa54p>uBcLV{6Qx88d1HnT#1(kS#MlzTB=qEMI03UuB*vPSc+ke z&@IYb&y>vJAim-Ho6bG^%$jZLx(YpJ0bezo=2M9(^#wEB=jFBn>omZ1g=j&rwAb+(VjXsc*p+j>GCs!Kv0wCK>DUc^?t`_A@`i+_0Wpw7UKZE1C0Ki&=b|JG zvMypZVqH?kjF4HnLhtu!Q9MKvj_fMf$1YvNc=I=`9{>K&Z^5;OlKkbLua;IhYR?I}BYf3W9_!@pRLY!L)G0lvbq# z&lV61MZ{+O2{XvFw3~G5^lpps;(|FK;wdC-#s-85~iSNX_O6&Oc+^;4pkEE_Qq zy*OZ@7)zBk1g7Ft)I*w!G|rAsvI3rk7DT}G$`CaMODY+zzItb-Hv6qDP8wxdl5n(! zW0sfi`-|i71^2k{@vF7*0rU(qCh}5}D^6M$gm6Y@qO314qODj64t1pgp^FU29GV_R zm>>vL)DCThEJJ;cAyO;7+f`W|vbA2w3}P1ANEcCL%Ix6@8G!8abJkB86|{~v6@d(< z#W^V`mO>s&fuG?*QDVa~L@`0`RmOxWsGbOok+fG94B6CIb_a8_zDhm8h)yAhB(y4% zaf~3Anv)FTg1Bgq`an>`I$VoJz^`1%J5eAhOjPaviw_FyTaa|NrZSeoXtBq@;2Jf0 zNwg5`*NDHWbR%Nf<)Xr9Tef=6SvVc!=ZECFRowve&5ty?mX^Um%xvJ9R}hwErbGdLXDwr zcqW`@S8indrK@*uecS&c?6CCY1Mgqq?9%B5vMgGvZVdADn*Hg1zkoDkU1hT;oH*@G zPDC*+Gn!|Gp=-KE+i>;z17L?F3xKL&IxGqrsQ{6SsH=)1qNtO2s-S>t-T14U_V1g= zt38^aktORJ7_DzYanx^MG&5aZ zb(QM-?14q$yfZ^ZFA#o@#!V=i_Ji6m8!1kEy{ND7YfEV!LIHe0kfNSQv;=C)-Gd4% ztJTf%lbd*H5zlEGfJ$*5?RED6fe>|3pX2HT>8?Cf z&<-rW5-q|v6;aI#W?G-760KeFzmbwM>BVhay0JDT@xm~7rkV{|)RvYy(a;%}1Wq=3 zi5aB)%Klvl9w#1mJWaqS^QWTYq2!ol7Udx-A6y4=3rdFEiMP3=&$+v~+Ocs~$xB{V zY3om%#;>^D1HnE|iY>`xuGv{T>Ev2#^1d;N$}@V(=xep^9OwIyR4BI4WVkG4lNoay z^c@fn=7a!7FG`oMVHdB?&Ds6>DQ|u6Ob8muFmHVBoweh&oQs2YY~8$1z4Dj&?YrT< z#*3aRTzIyB;%Q91rg%4sfM(M!3RabvAt`}U zLu=Yn9R-9HfuebdXDTMeTA7hGM%M+~)M^c_VOwd$412CK2o1Fc@t}$dc!FOz$TkAL zN=lWeJs^J7U*%Z|aA3(=L$z1@F1(-y)h25St>r3bJ|`4%b3#%i)p6pM)r`upF4yX+ zF|zJKl_xRga&QxEu|6nc;8Mr8yp9kEHtMGA+=EDQ&MrqE;!aAI0tmf;XR z(lYO-j?BshPaA6(yaB@PtRW;H~?1n_WI=T4EPBj0$Svlg^(B~ z1B6A`RZXI8D-d`=k00dgr#vBWKNNNGVI4b={vX4RYp(kFi(mEjia=3%?sJ~?@;`a$ zi0ih!{!%y}h>0oc%rEy^+m5&DYa5{)N@tvY+TzmE_U(7CeLpN+{*))5|FE+&HkmY9 z3NHgwM%aN2u&cjhS8m9Ssr~W^fAZ>?z$ZKZhu7UXb()%UNz{#vI~LTR+>&qKUqUH7 z_woFBrv=+iVr)zHZzJr`Ezp0N$_RI%wP-a}2X^#(L%x)FL|=&2f>Q+<`q_|FC|8M7 zEU5~{xOUA4Ah-?7IY^T-R&UB`|i7CTawh8HaFTK(Q*i03^swjqB^Z2m73B?h+3B|76+-JT@5Ou*>>|-ywx9T8~kK_%EPCK4k-@qtc z--P1-l?{wE)>cIR7r#QFVz0YVxct!)-27;YbYJ4|*Oj zA$R@ZfR_ad*Xm&}`|q0otGnG@GAkj@tR14MooO##wG}({*dU0t37%Fh;cS4cCh8DW zD9E%vbGYO|-7hc zBznCuCw6ZY)1pvXw96=;K`0^vr~{Y~j3^2t;DRLS)5Zi?sYc+=vn4I)1`~~nkfoRD zWWR;j73RgjW$3i zA(j0!sw*bqd`+%{F+<+$>K@=SIS=@HQ(FiZVO0nbD+_(yRzj!<#cT&)bsn$)5tt_b z0KH?1p@`#U!WCQwnhy2`auTK~M{n8KFiD)mL5OH+rc9a^$$YfRF|>@JA4Y%215%~PI&lXPH!+6xU+m9OixFK>;yji9+nAT zhUTNeL0K83A&r#)=+OEAVpRI8C33MEGJ~X~-FVBK1zw1dG1QQ(R&7#&FL&R)a~70I zqx>yJO9+D#$_)8_g@$l*FA1`u*lZpPf&t^F0TET6sdKa}{LG(Jr;;L`4-VkAQ68ES zyoN%3U2e{3v#3u9TEqhlalCpJ^*a-7pL*YfI$y_*|Cg|1wbOahvtR1_YntNp(@%ZZ zd;S`l6F)+~5q5AqYPD?Cm+_;jTNWwx9L5r9XJapyVek zME>h`8p}iOxH;*(la$AvPebxc1hDWxqSfltLhsHBI~p7ZK*+jjH($dJ89XYAzzR80 zR44S7K-i(9?2w#9F9|y$-05py`(~AQrrxMM`AJWv@~mUWk%t`xTkg7L&xhXnv448U zhir!w&wRxl#KU0?{~(!5YuDJ*DGfUVV}npxhnm@TXb8>bKv z$s7(Q#L6&AKxN4X(C{FG)Uq^S9{-|Q$zEvgZo-y|5UrG*JJ|6vW2N!EtHQ5-on9`? zOqOqb{H@$xhyUeL`HOqXEaxvgB|dM1t8qz?<9cxGBo7icUrBU9X(;|~nGJcZUT~t9 z!mbiUfth6y6LN?apm<>d_m^o)RT}9VWC1t~vQbG{$_;S58@E@(WeI$nRfn+$8>3;V zCH+D15%j}20=lJH>IMPgu%T#!$RQCc>nInjRTE`HQV>7q6f!tRgCqqYc&8B?t`oB6Yk)%lY`EA}}5=s7a9t49&%W6VV`hp4T9n7}_5e-Y1p< zj+?9rg{G9i1PQ%%EjymR6Zfh!eCx9xHo=!w31JF&T)A4w1@l~JOe%ECn3Tph%pS_Nrx5e z(0?#>+#cYi2ntFF897Y)FqLU-#u5>c^`_E-!K?(!;J230fceRX&At6CDGZ%;?6_Y# z+OebE?)>y;Kc8P%9BVaEwNHKevv=*f+}=@7aI&`!9Id1?T_X3!VeE3RkU|8?_q> zLRu>7l>NKe)+vcx|JrxcPy9Q*#oM&8_`o0E#xJ=1Ei2_sG+I_T|M>XK8DC|SaD*Me zvMAw-cA^S$O$qi%Y{aQ{!3i`UB1>f%8ZD0zBxy-0E~^#*l@yp53`dBvP^z#4CgBIc z=RWtvQO-A?wDD1wUbv1OM;dm-W&D#Xuliqa_~1wX;S;-e?9H-!2W>6RFaP*^Kl}EV zuiSR#c_(j}K4P&$$hDvR;F`bs>%aNtx4+fxbsu`sS*xAS(Tg4Yo8I_Z_|K^DNQn3d ztoXjZ3B{4Q2}PV0fB#QxnlfXJ^2N`|3qaR{+k^rk3b}>ru5Vzpz6k~VYkdPF)F)a3 z|HHx2zxa!cA^{l>9pr!WCKSN%leWlL{*-RJV>kDtOW27WN8ctC-hpxq3tv0D>JB9szn43`M7rPXk3*>xLd1by=$evNSFDma z0hy~JNO0;nkJ8L_l5&`c8|pOIa(;e=c9Mp_Nu`r!^M2Vw{tBEAfTs%|Fmc)#)FAaA zsS{9iR2?)zFcJg_xrQ_XWJCxO6E<_*jxvbFxk-5M+QqmWh$zsYC$JfqH<}#i$?zEm zKdqiw6r7XzU{-SCaD(lvF_oLg4E^jK@5Uu`#ykHcs$FwinaKP}bK)7Botd#>(^$;< zG#|Z`s!*5!o~8^%#%5_o^cW;B;xdVS?Q)x8es<{L=3_W zeydBfvlF~ng6&qBh3Rl^3L(u)iOV^=SZyLVHV&NZm9LgZQ?@_!w|?`syUJANkDC@x znR6$b#auH1AdIjBG>c1z*lQhdfaYd2$ z{ZKV{+G&TOrI6vK#Kiby&%V81{mQo}TMTpBxhJ3V@H5x3gV^-UG}pn|81L4Opl3<$86tO!thQzUfHyC^ld^hX>X`9^1D-#_9+bJ;Oi6&N5K|m zjB=4BS9*ZXL%o7xE?rwroJN!NsuZTdK7S9&Wy`gkmbXgqbR5Sg$MHALP1}}HGF+$A zt2b+!CLxxz>K4EPr;Q$jfA9T3Z3km#q9R)I55O2;!)P$vZK2&Rti}dI7++ga5G(=| zaIFuV+!j??<^Q)N!0kS74_XkC0AJyyei+yX$MZg5!aV^-$VNMCcj8qi8TO;Dmw5sx zN+LJwHCSCg=~%Lj8UQ4Z`mP@^Cuxx$pylMxbj;rk^nLA%2Z{VX;lmG;m)EP)-X zBI=C^(v?9vHm#M*tE)UOMM2^WBDN@In^Ih?3=6O0^Gji&(HgqtsH+0{2o>@x(Q+~)L;~6qLK?z zj%>rT5SLcG-Z1vT%gchdoqhb`PYb!}YsW99D~ zG-+|VB+5}mGPD_uWiEhiRDWE$-(7-j=%S>`Qjo>q05#RLg*v=d6bpr;ZmLeykAQ$> z)=&F`qMz}RD05OK^_D%~D$HVTdjGhgPa zAt8J+3QDL?h%{UqN7PSaScs?jYRKH(2a(9)3#2?z&aI?iyN{@|a`uXaAwF6tD{(3gy}&Xz_4E4fu+- z6DKtR4uE2kJQG0u#c8ui8zQqpU7;moLOxMhxt*`BCf#1W!!P$b}TFBL~WWtoGckK)8iA8r0m_l&vk>9)ebyxc4m?ghC)gP?LluwinJf~YKjT-uKtK!>h5#E zSH_LWhBN{BSN&NN0#qU%fX+a`AXrUk<7u9+M9Y?ByQvfAA%ef3`>>z7K?wdkQx?;l ztdRw-W5*~RUDy%j%QxM2(}(}@-(LI5SAXtvpWm@#CxZOw@96L&*Z<@p0&I3u^Yj!N30bIG?}0y$ptwV#6#G@bb`K{ zkl_J4EN`A)xvr2JfqLvp+J4UmnOkn4(cI5}PI6iE&ayoypL!8D+X!r6hgO2mOF$>S zuG(O^-E^f|P`s#1x&%@s8nUA1;j&TegN#i{}vaPe|6JsKlbCLh(-@W!qgU<{Mwmk2TjfFk0Wh=&0X> zB0I`AFq)ZQzJ4WDxaVe<%-ng@Z9*|uKZZ_z5GhUuV1(SLxfv!Xy;01;ALIcU!v`G4 z2ewShaFsJQWL3X04s_xUoV2Bk8Jw1}5k!W@(E9(8?}Qx@KSG(_JMA2nIfZ(IQaPJ*-^(Hi_fUlz~YSQ+2ZCs@y~A&=VmxVP0AvO64whjq104X zl#%V~zGWGek)MG3@)L@s_U;n;_Y^Z11jebuEVz3){Ordj;dV8Uow6fCE4K>Jplna3id}v0WtIPGh&fX+T)KZd)zS!TfM?M9a8`I5cv!#Ou zNHX{&IDDS7iY%Iw317h#m0j*87Y4DcFhzj%rpF%vOITNPE zyjKp=65y9)Y2Wd!x>>O0jy~#v-)Ke0SQ0n`oAZdehrQ&224rj3>)uq~C{V8SLd zv}wu~Ne-9K6dL=@buxECX_`XK78`uYcgiJA5P~d*=eHyq4GrqTQ*f)O4p=Z0sWMS6 zUC%2GMx}RNm1~Uh`@o)yMB_@Lp%eL#5b#h0NgPCwXCPtyaa}S%!j_|zFbgV_PeG{Q z(cOD?Z`x4nC^l*hDT+vaA*cb&1s(`hXuQ5b-)x8m@|g*Nzyr85K&_Cg2ln+7FFR)Q zv?Mc%!j3h^WQDqTTdg|HQzznD_TyeBYF7r6kE@eVk`%McGDzcLL3aZ^>#$;0&>=1@~ipjG> z?F2Uu@7x!E;BTf;#%8m4|KFG5zJ+oS3?I6opDnh;rml5_QL~VaM+6d%yqv zpHx|7o__fg9`lHYuVcrNfE~TtfAEBt|J!Z1SF6<=OjfP-q-QvFK(0X3tghc=)fa9a`I!>_McT+)e1*IVgW9t0| z%TgQz?#jF}(hs-~az6SAHw5*MiU^?tx839Jfy*QQTaq=QPS~;?hbh3+Fdi5$MhVb@ zt57<4^q4fs#6qVxNTQ@xH*<-G(vZc{#Q1DXY1!g$Tc$UFG);#ZPup}J%H$})ooV9% zCWY^gVlvE392wHVgo^jlaM5jpZt9}#B&-tqZqM7arKM>ibW{@OiXl0mlT0MDi6)lw zjy|rU9%xlZLVtP?N`%(Q5PKEG#v+uAP2Hpi<&!huH%y;d0LKwas!T8vOSJhho1JO0 zAZ0(jsb~+FriD<)3UD+59z@G#@?es*m?y`;Gv!tu@QXLnX0$U8XY`G63?k%w;@_LA zEA)QhwJ#O6j_uEG+E>K6f6lgHwroqX$jM=zdcNngVBUhjNPg~EqNNH3&2%Uvi9ww0 z^Kqwao^rq~2N46j;%Sjsm>Y#ks0djRbW=q}R##m_L3+i8G9z3f3WL36@rvJZldvpl zh$|vk^#YfKL{g+HL0c{L0I|99W!U1l-Y|eMYz~O2HxQx_A+s^VR1`vFzwq7Meqfis6N%7qVSiEeSXB`&o+Mor$^2f_g#faAqMJ0vqJ@dVPra62%CAB8evsz_$`E z!SzrxKyY1B;dc$iA|Ed@2t{NDdWJDd@QOTS6vpI4RuQC@20A9ooCK#PdCsNa8oj98 zF38ck_$_k1s~CTB~d4N>d0c0 z7PN!vxbrJ%ct^}z3--xfKV&j#$8K}ST@(r){ivmrXN^hC1V4affdimG9b&o#WI#3D z9%h!axhSy`R2jIhDQIzyfG#6wpdlb*G)@tq>p5DBm-;YaD3zE*#6_VH7+Be05}45* zPHrHsF=@yr0Sl5%9lWDKF>7oIAqY=_0zBX?JCToIf`%@*66mS0y2e@>*GPO=Abf|b zN%a9(8N=zKjyR)o+Uo9mHf@?5zzBHi-YW|x1Zyrz$X!X|Rar6w+fM^sFjFQ8!DAy< zWF;}xJFBj3>(kRU4)qF~!XGXbrBV_qO2|c!{opn$!E&4@retm;ByG~zY$k=}tOgx7 zrT0Owfgpoo3}M*#Ty>1mV1Jy0tTxrwggQA)2VnjccoiudS%%b*T6JMuF16mOH!(3e zs*2y9!q8dAj{Bt}96S1}!`tt?>0N*S-nYH=9sl{CKku}O&K;f%!)T2)PdxDiaP{GE zsO$RU9((DZzxK~nO_d9mDn8)6uTWvffLTW18qp`@(-cjyZwHq7GdZY#P& zrfw2;AUaSFF6cPT5rRk3<-iV3s>4v1Z{^C8ojjM(W|IUC?!D)4T8s099e?{a=ES-A z?DqLGDT0R_8oD&n!oy!$6t8y=CKI@Dv~WM)p+D1&TY+&{p1H%jZRx$Ub*x3 zJD&RFCqrsJcfzPWpTUm+Bq`j;=i@lXF|`~BXXS?2Rz{H!z1J^k)G z?n%?N`|RAi{E%}WGP8N==)#V*(NBjGK|&Rn);FOz0ym+!>Bi!=I|=_DacObvG0^iP zVM_@Ok4);nwh6`h21fk)21e@}82#VegaW0`ne3N0lkb0P$JQr5(kW`q{0b+`zprL2DEs^_zhFDV1%WxMXD5bsu;f-Vf!@!K}&J21^oR?i* z%LLGFZb}nC4MykxriA;9J~-(Rk8{(ZpSu~8gXe*fTapdZR+s8%FL(s-A6wl;dY2xK@y=|CbfzAAeV~57KLW31%}E9Md+inVJrkW zTPTcdsChrIRDo|PG!|fV6EtoRCTLO9(0M^*2Rm4Eilec{s01Rr0>jG)7u8A-qeM+( z>*Cf8e6yDATV}TJ;}t2IlA<^iN&yfduw0rlnyiRQZkd6s^Z$4 z^KX5drcRu=t^AYU7lr<9>1{o>rY5JH5GDSYqJ>%FO1~pK6_K1r210)>62r z<^~Vt$uW|@SCz$Pttkq8KPuZHEHc+bRM)7ssaH@{B~=yE4529X+{Aa|;V=ZgwM?kl z0(5N z<%`lOZ5EOk5fwLgvLPd6psrc`C9R z8Wnf0=ApQ0c~_U8*7F&QZ78WGL?Rb}yVO)(NK-bBS_-4-B|>xt0nEaFIF&}`Lm{Ta z+#ePLwbV4u$Z0mQ+fao`Z!g=wJFT5IFgJAKFv#^V*SuB!j@!uOE;x&yoXP;LUOrsT zSAzn$AjmA=FG7VAL9c1ZZ7FzujnaCRB$g3TQX#5anb9WW+;_rxg{K_Uzcy067H3n<0ZN9{Vv&`TVct19T=8#&l-(m`aynO^DwO_DOAy`@3F0FH$=zqrz{1QKKw%a}U@7{vM_Qm#Y1|gZO8P z=4!a26#5!=1oMh4Htae)&WVc-VMn2%o+yjBJ&kfv6iHEN?TaGMeE6Q9Ty@oTH{En| zRTSpZr#{M_sjXwj{|)TeH^2WYpZms-zW3M(rqCbA|?_Y54xlPzuQ35;s+*!?OnSO!gPuRAll=&O4{nhB?UAy)! zF0Nd3(M8bs4+cA~zv0GbJ@b#g_{FdNrdLZ3J|%H-$FH`35Cz_p_ctWNoZ zKY0H8{@1(CJ@@RbCm-|pCtdo*Pk(joH-_mfwJ(3(rAHTb(Ca5y{U|0L9v|Tn?D{4Y z2h$O<3B|Xr;0`Pi{5<#h*`UnVH!xb?z-WB~qa$Ghqs^PMtAD{cE|(QNoBJo6!W>1L zP(1jZ?}mOypx($b$rCg&9zgLOk11gg)Q$zc^P?ESWC0T_mDX0&4<4PC=^l4iH}0bB zI31=4q)6hdZquTwszQq$(uMeqLdn{PQNm{`>0xQNyQ?lYG(pR$6Q1>@DSsZT^ma5B~imO})6oH4N>>2V&)GH+>$WSXLAF-yM?+Jn)RkC zNlQMTm4#8gfjXE#Kv_)_HBHhq1#KlsQm^B(rD$oojB^FaRCFQJ+Doo8q~!yfb-QL; zmJQA>4tl=V1=}y{HG_QN;CpmPWz1Tb6ru0bYbIi;zt)F|dnSX(?<9j>*iUl6 zlH?{nBGQ=N$cr+}eL?1mA&1N;I-piUAcU5PvU0y$?sd49R&20Y8S+ek&8wEIB0?23 zt^g(!_u4+{79}D5#wo;#Sy?ERAd6x)<$%Ael9f5o3WaL$7Psen-1upczGavVBeAQo zsR5Of3s+rDu9Tj#3s4P!epCb6i#{A|(GpV0oD<9qyfMwyHZ!%&Wzc2(WhQZ%!CqFf zIi4&MBOraA{6{R%|i1j-m}t?#F}WU=>~`@FK#==t59IdT~}H8I#rJIvR1G zw_^?z`81JD6rS%sll4m%a0E-}I#CTxv|7s>$-9rBQTLK()p;+zX{a zHe4;u*=qe|cs~tV650EY0HA6?$ZuHDjLG%f;^y7ZGr^1+7rv#a6?X7^G~iZlfDT}% z&MmERR0k?1jA>Nc>t3%r4tc;oy|0`YgAd2a%}abMDJC>c9XCbZ&Afi<@`+gT@bwhr zN-E{NOc4H)Jd$P3Kr88nAezkTO}w|L@_M1tR zUHI?|aPlGS`1Z%&_nbHUhaap>4u!gW`I}$$`@jE8E@e^9nF*R5BvkKn*^&lNB}J|Y z^2Sq+@dnQJ+joqvy6v{xRaJi2!_ESBum%6Y_kQHhUiP|oyyNe#y5`!`Pd;UO3P!eu z9amlT%O^kadCSYcaZ&Tb6WVTWs(JeH8!o=+;ZJ$`Q=anN%N~B|c~;F_nO}{fcLV=9oiBOD3$R-pY{*(}e+*q@G`3o3xeFLNQ4UDRx2k@Zv4UGPKH!$*2 z_w+NuE3c&YIC;iA@si{y+5qc76u*Q*rir2tDg}Xq^XG%TG&PbcS&XRTh~{FWI*T$x zRlwe8ta|%~Sk(Kf0f*H&ZJ6{CCkJsKXXZsZp-gxCuAwW2u7Q3b;2q*aYfn0u&|+(! zC;>QV-X8%K?2Y6L-(DRKld`MSl7eli;uO);-y8=YC>f%$*WE=ub-B#_|k_v{>NG$xu2KLhs?0lo5lJ4k@dxHG>Nprt{ZOL#iiS|1yqdCGgv4x-%X63jkMMJ0AtS|cW zAl$a1)$6t8{k<)dGrBDI!*CGkmPD&79;$FWs|x$%AeE>q8Sj9kj+LTOb<(mCXj`$i ze%bw!mqn|^V!m};UJ01%cUyYX-ZBTLqc_D_zc}!7Ms9MOFUlDMcLD^mg(S<&SH8pi z{5qOkjQ0;TBg-T5k^q?^_tyZaW!N5&YrN_ zVfTC7qEA+HLQ|MB=(YDKp%QSdE|@$kErs(l#sA7P1VSfi4P5 zznykm#;x)7mORZaUyqwfm?d5k6yR)pW*HK= zB(y&2LE&lHmuYRLo2;UyY@&IP$24ZAoh%>l_ECt|wH*t&5$?4oj~&FZC{^`XaB?+p z+A1atrhx#t++MM!ghGe`*=TG08Ky3cu$F{TB_N?Fy`qzpPDI@b{RwOVmTn*#F`G{J;R)7@P)wnhR?TRKwPSk3qQVN-o7BZ(M zJwD@OyRIT^Di01v(l}Vhj?v|Zry~$MKssOfM}K(RO*gLnOheSQr#$8HfAN=ZeDdX& zKk?E>wd%D&6gjbGN^I@E*a5`24|W8LlF}?G!({v5Kd>@1_vrk?lb^+%Vx`#Jl2=3K z=KZGHur^Keq>m*j8x{+{V5A1O;ryJ@N)!~>QHqkpe&W;Y_kT!5e)1Eu%P-3aJGxAI z=YIVdnKwajr+6L|lsrPoR8}ONpp-gfLWl>lAnNQO9)SC+;K^E@ZyZP1(cR9i{z{HJ zH6`~MJuZ3e{*_84MIoRQUi_k0tsP>Rr$6->;D_tj@qZ9ISh0BhYyRQ`@Bh~vS^u?k z%k{VZ?AohOy7;(BV+z>u(f7XnC2#v=cJNF|mR|o4uR8D14}qAgS=J5L+_ZViEN$S! z(by|$ah9+H&13V%`42mH&yKyjckdfr`}3b)-I{6ECassh@U@@*UM{1H^^#Z$JLoFhCC+UCw?BD5UGl4?}fGRhouAIe*BD>00M`vcqT%VEYOR;k$>ey z)*m>HS`EP}$rD6QIMn@w0c;7;aban-(K2*P%}W{=JV+sW14|e!T1=_3S zqKu=T4o^7v>-qsHLsr1Qa`>ZwT&f5a_f4>87y|$WO+Sb{ORB+>dydCMewM_|M%|~C zM`@seDoJ_C25w?n8po9juF{Y>nTt`VQTC8kJS)wL5&E)<#khM*Z;`dPj4%OOD=d99 zE-pdFVCr-fc>ohF3ojk?;=XXwCasqhcP~g&P3oX&Z!edA$<$9wnrAY625+JjaJal+ zvpxHZcf5~^tf}SeUY1&hD2hUI^R8_7qVlx!vv9SvAR$;)5L!|r_Dit&qDSy@0x%rLFf7;OqpD7pu9?{0@13t3I!;q{};-amnq+O}G2%7Rfy zI*7NtY2z5k3!=!{8FVSVGRw+npL3w8H8Qza34tWK}#Q8U0{>YAz&NE!m8m$)^hHXqDKxo_}RniPysEYPBgv~dJ#3?Zc~Q(H7s zS~#+3$qflXuyY$bcX7aTNf`pLU@8TWhi7vjwN%`5yM;)*XE8hN+<}|Mc(@Y1G3-XH40GZUiIm$K3=PILnMO9rNzc#!fGIA&8R<6(b}$n`CTKCcUKv9*{W(T?$qVze~y# zkw_PH4rf4Y~qAP0%CLGF?EIbQ22a@&TE~o;%D9l4mk_=VW3cJ_yGA%+8 z5bGY~4ypscg9=^8j_L-Kj!f(@1nXsg@@GH1^2)WJ8RqPVp7G&NzwhEp9<9i15ZZTLss!1@I^!?`YIWWccAo_S!-hj9dJ4c$`*|aI#gw8Ei?2M zgVfM)kPL<6=9GR`>|U0}>w>|ty`AOCElh1&T0a%of&31WA+Q4+@9rJtt6xXsa1Fiu zhrbOiS`Lm+@7R~`Talk|RvvXqbDZZ`l@}UPEe=X}C56cnZ9oJqWkOr9Wl^as)!Eb} z?RS0RW1sU}e{re(?;rcj+EIr2yMOr0SHA9##~MwM=W8|l?6XgQ;u9Ww$zvXU#+j#W z+B{pY)B4|p9r`#NpY7hs*_(J?%8T4f{mG3}4|~jc7d`HRTHRDs<@i&!z3y$VeC$&$ zo@>nkF8gs09_r;Vd&ZCr#A<}%u}$?WzWSrAS_EqM?)}ev!V|`(CXW{Epx009n^4>@ z;VDPpCKPNgeD4Z+&>5%k7o5W&9;|O*w7!86Y!7LD1Eb)-djq2^FTZz117^8();;z7 z;3(R_=)o^N?gv9u80;LrV~aK1bC~vJUJ_ZErMas7AQ>1q+PS%?ZDW*40|<3c65y~d z8k(RN5$E;d>FEiO#L-Vd;V&mk5F~&%P0++@c?(Dyly^-WGQ|ORUs`7L&JMR?*9_kp)O1&?HBYBYdkThsKftNrs zIG!)X6gL8mvm_gSy>>mEE-yOW-q3UGnr_zwH)eNxz@cDFHf^zvg0G!---M=w==$|; z31rnzJ!Iq8r)i+zkiTesutplbM=NRECuK$EC1IS$3VtgQ`GsNMiKDpL(8kAf_yTZn zTyGRb?nJJssh*nwUrbe=cNvK#J^ZL20VWW&VQHvF`{Watdv=xk=Xqs;sQ$!5nc7xzOvElehxNh^ z_skcsc!LT@MSA{ooU=}rU>gGT{B667fy+N+qV!h^`}uMBl_Dam7>qH?+8g;YSN)v<@f>&xeJc4N=bMs( z&^{Wifa;ax5@A;5RFRkF_pi#l;rCJ&nABpkIHuh@`k;1Z-ruso$=cu~CesvTj67L844LIjvwlm`k2nm}tDblbzJ zR#O#$J3^VTa-k<~sT4XB^K8b8sZxeQnTmOobF>DE!0W0{s}hvM?GP}|rx$6XZD&q9w0np}e`xr~}0!9`G5 z6~U@%KC~K-&;_50%OK+t6kn#DDb`#J#(?txFZhgjiuOa!Q4FVWAVzR$pd6!UF)W67=HbYOriaGdWE_KN<0Qjg+(B?dGpV9Jqs_r2a1H`5JW&-iO;ilQ zwxycLi+(mjJ(}`{!n^4ZNQXOXiX=;X?yBH^r3BP+!z|p?*j7F5cci7^zIE(ayZ%VU z4xIP)x4h@GpZ)yWZw&K?fAsv<{MDaMSWP&A8+(ST&;k@ZuPYL0p2R^Y5q6*vfgPhq zAH)uP;~1sJwr^pyaT+{Zr4e=%1xQ5#*f@wC1+b$Zcaa&y6!W;P%;$&pi`I|Q;KL@ zoM%Z?=(1j62VwvVL5H|+$u`^vSb}OowEE}@KMozbUQbCv^kgnPqMi#g!IVQ-*H z=JQ|q#(UoN!L_dp^G7dw-su;fzK$LLr?BIj-?-vWfA7y%7VjMpWLoB{U;FYGzwi$) zeB8r#@7~jG_eRHkFZlF-eDRv=?j5W*IX(XOAAQHht+Uj1K}JIf5~sV^4~sZblcc0# z26^O{VV$d?^5LPfC|`KdBYyIqR}BV(>Wbf(@;txpx*Nayogb`yXP7s>>Cc|??59A} z;j>$MNe)Y*Rf7)44)7&@6mTS+_1lcU${KSeTgajWd-l>$Gu87RcE;nL@`y9fKf|<5 z_$(qJ44@ZxgUmxDggu}+&^}q3`~9Kgx;wV-rW-QMU}fR4mp$cZ!4CelZ+O-ECKQLK zh&UC(_3|=X2P-4 zJ;S|-il%HBl1lHUX{NE*>A1S4ErpK2>C+k&ir!!{6zWkeZr%rb7*!hFtIQqNUt+9H z5nwXSaolyIAOY0V=8Yg*VUj2!E&E~0wj#=A*fl%95+py{4ajJSXI+#_Hmnl$Moog7 zvibgf?3>=91R;Gp@9gB+PfO|L1KVS=guC^CP_Gxq%@)COPiU?bonAI@vmp2Lnuf@% z4AQPFiMCvWN5E_{n$ldb1@A%{Av%+sDBwF~nWEJgb(&8crFU~6P6V8;0?|gcfO^a` z3L7||jIHd3Loe}}3}!7EMx{zXBPlk*Ab|d5$;T)`l%d3FQ?mq-@2z?xT&%3J_G}@NdWo4g75WKnD<7z%2FLYS3hGU~fc5{2=j2nAW=W6 zAh?+{?6#8tSc7;ghf(T_mdB|juj)Knfe#qD5Y?YVj91Y z(8zahWwjTES*@las^D3WPY8_2w#f5frKY5#e72^`8V-tKKQj#7$p(x0Qc#9|5xALe ziw45vAalA|tUL2r7Iq~u%xSbpL@V);se>-z@ASs-hM!TFUY4+@o;8fh7+M7q!pR+I zU6s?pZB&lD?-1cc3iBXgqMS`k5v8O09@*gi(z4%!Hz6l5?<9jhLimMcrQn1tK{Xi( zAR;eV7yvDkU?0n@T+isUq6%RERw6_DRYOE+@F{W~r3o@mJyOs-)$%J7T2q&34~llT zcjJwB0aL5ro05%?g z-!_#oo)H1oOa2N74~hv^2B1gufQ&=~A2Db&Ml1-%534OSU*HiqFM9>ieLH?TCE7-5xShaYf*q2 zJ$9-HR|kB(Q}hs6ygUSUSemZLGOPmFVaXOe5oVL8OdJz-czz~OFDIS>K70^6lu17I zl))XLFcxx}0(h))aN|w4zUGyG33W|h80Nx@E_lcL-V(sa*RkXOZ|umk?62SY?zjKt zyVJum`NStb{_p>aJjv2X*Y>uHvP&vB8+oEVgmSGe#=h0}l?HLZ5hBOmeIE4~*56s`}YKYGdY{?|Xg zGl`SC?z#t>aqZ~AG%+#uy0^cQ;aC|Flx5P4x*iY&@bBT+QKUjqvQQZaRD!jcY2VCf zQxI)Xk-!c(AObSHKtL!f83;+#Jld4Hma8F_Ew!%EeivM~Ke+AI9Wtw(u=V)we)oHj z8~F9^T?Za_*<(~&J}R(-f5YovLjhoY6N-CNnH`avP#g&IpM8(+Gdszich33-MyQ_k z4UE<|FhZO;A~rCR82uaHA)i|6sL#LL%cY}c1EVQ(^V(&BS9qgzs2%`;wo;Q>Nt1L`IAmRa;Pko!RyEuL*Ygb+!tY9O=uXn3WxuMpeJCO9 zAP6YD>?DJR+yo?3ae$+oNZT_?^j;pVmZ!AqVyT5-WKmXFl7Zm7+U|9`gW0+9T#6fN z6X@$F$u5`MYzgBEM96OwHayL2F3~ioxGXP$sW=!H`EC#QUt}2`EeJ7lI7p!;Y5Yd@ zVug;=LMhd2R#-yfSrQnGJY_b=^`=~^os~duHkUH?*d`fXl4lF<%A_`K$|?#-+8ZoW z!kzeqg>I|CtwazIT$D3A9Z?5L5(DQDKOs3rk3eoYZHj5%$jbFnv;bjo5`$rZQ6~r- zMHy!a3$87wNTRb>Yu~E5`(t&&-Et-rR&wpmtgb7wGX=beOX;O5TkhG%zVV%MpVsYV zPCdi_>sRTvszbpqr;E~RC~RMqAO4WkkojO|$XNp+jZ0bwkPQV>)nY!1(#TdVG#P3G z@u5HTv#f~XtSlL8t&<7nN6`R`mi{%j^65Zvce~vL*nsV7*_l17>FovzpK1s!ms z&Y;&H4tfJO2ztGN?+2b6v{wg%LFoB@SVeN#mKv5FvmOK%Q2+IKruA7`h8Q}K#ydf3GHOiKrB>Ym$c{%GF zilRYtYhtZNZv*>%j}@!8k1TCyO{9+oflvY}84_%Y7J%<^12xm4IPz$Uc8azK<3<6eWR`=rD#*w)m;=id zK}idJrZj=8;0u~s#X}526tLjwA_z6xxrJ?PEot<6xpTNLf}Jv@s)AXdKIoodn);~^ zm)cO9He^f14;*oVU*34j+|2a!^vrmx*=p8hRbf|e$ynVlH`;uoy*NnY+?)i&L?R;< zfK=+#$eCEsQOu+VE-^!MA3#Xb{r)~!`?NOK5XTeF1IpBxDK`o`ULR^7H3x1CM8KJd z)dbEB$qxVYd*m1;S$G5)l{kOj5oui+S9`^8a|L(nSYe&5?tz}5y zjvxTk0S~PQ>{wbJG-_-ohEU_c{r9@COz;hA&>#yn5ZNjbcBm6f_f}S}F`|)VgdNrE zunIeXAQ}B%5a0od;LWSuyHtN6R$AVsQzgMlZ`_;Nsxmc6LrB<+WeGcW?qXl~Qmxaa z=6mQPynp_C37`P%SWQ>NPRQ@E zkA`jt?1&K^OUBSgBgb*}Vc4N><}ye2_Ibio%m`YA9nX92OPwK2RzljiarT3sd2cBf zhpt5ty#YXI|`ot=8vxX z#QXl?hhO@1cmMXWIaVJRekXP;Ev`QE8GrQkuU)bBc<3Wp`pdt5?NeWJxxttbm?+3E z2@v-9Gmks=$`9zD(FcwVdZA>9kV2MdqiG_< z_JT(A4Dx|c@+zm=mUZ5_=X~qi--RN>CAy*g#k*g3`7<9sVYM!L^O zBky&u|I^b>J83oSsgQ0ESVe|F8e}##Jj{Q5>+U8Oq?AYth zgG8aK6&Z4nfi%Ny5nXAl1Ix|W6S`!mXjCru-Q<>A@0y;Pn3$ZJot|=C=hj_luH+qv0mhOTkGh zfW9gSZfNr6sNVu-;slfhO-F%P{%BdI?a zf-8(q)#B2}g~+*=mACYYF|}waG$DmvkFuz)004B*4DU^8k>lx5FCxucr=e=SxVsdh zVajPu0y8i~8qu?M_5&xt3q=q3xSPaI?9m>GaRj3rtj4vPy^x8jn2%TZ12<84HDxlY z@QVIc87dW30+rzk(d8iNgnpcXA0uQNLJ{O%S>%eK-kW)%1U{SgIb#OE5rk`mkGhC? z;*O?RsKZ-w;DS3}n6s;m6P84|7Z39ZOG~-p$G7FWC{B!l(6L++gvA^FT3oI4!#NMh zUi+dFgc(7&UGyOM$5z<#_Wk8UPmIwJX`e6QbsKCUpHasw(HstpSnY*-W1X0vIG zjhoGuQT?$MO&T9-xL#=2b=fW$K8Cl#V8+F9dQ(FoS8R&o2rTdw1XTqP$5E?2?=f}i#MlV|iP zD?`{vBRAB1QZFX-vMzJB%p_T_rTAkSVpHivfx(#thd9KhRd*8%3QbtjpffLx(jZ8{ zGiirRQf?vua5SR?37~**>KEadJl1!dhzZgx=0G*2t-I6@xF9Pm5AjU(vUTn)hlk^B(nL?x`Iv^wmB z++<3zDEN$t=!PXR+tC}4(V4@@AGHNQ9AU~UhHx>3(;IvfQD3aK{zREMF6TI)3|dDi zySJ03k!sy201nob8bV@}C5p_W?!zcW1HrJj)aMj|UP&>)j)d8hyMQG@40>FJ==F(b=5C^e&rQk_{?WM_R)X;)|bD0&o6#@;O?834;=74 zchcsLolqx@QfKq;f8(2Pc*SeJ^s$fbzGuh&ee>5}_p8r-?5jWf!F6$(PtLR?MR`E% zh~wZfk9qp7x86B=G}49*vmg4{d(J)Yte}XRQe)_OF%xE}IUtO{PHmVx?~-%w`qkZo z&JZ%;QJ0?oudlk0nb-pC7?^u*Gdqjlu6e>aU1maKjhg;{LN zc)9?fgbGI8M=^K}PUAM&(@+u;8nF_(7W|Ru@8uU@NxmL)m6&&@aJVqIQ~ zUDWvZj|%rmcrKOxka5#|oX|^$rf4q*i;AGxQtj8li*8KaU;1F+a;2y$O0}b_>$sk- zs&&H}ZF`EE*ze`C$ZygGG+fT*HKFdheiTJ$aa0!%lRKCIf2c!|lZVMrtGX_5q#t*4 z+@OdQnV}b&ze7JDA?osA#>3c`I1X@N$vR9l@=;FKZmIj-VpEM#`4T1#8iDX{NlGUe zRBJp_Z4+uUg{o@Z&ah_j-6)6gY%s-5h5|&$%7jKJfJIcTStS-UgXwQCq(&*|QB14? z6{ZTT&bUf)K=gNkPG|NOZsIJ?I%XwY9mGSlU|b}^F%ueO|JBkmQ+2*m_t)Q};E!xIlfCr3Ba#(!T2L88WqmRRB=7OapVkNI>9pk!H$f z314Kz%y`SFzM`=p$$~7xO%A=$zyl!O!Zgx3D-Wq7MUlnvNqcI#Zs>xd(nxfUFGMOL zoN?_BY#xLU8VLkd>40TmRTC~UMX|Rc7@wKsDaAZ3Tr3~gf|Da0D6Yw9QBs! zZsq`vRYe5%(h!Xq4*4Ntt&*WBAg^(nj-2%HgceE4q!cn)NNqK?6oi;WF=*Ls)PNKF zgUDI+`yvl z;KCra^d~FwWxtJ>oIsA3DW8i>&K6=;z=UClggLCrs|nm4zY3<=-_MAKPm6p(MO2Mf zX$hoV+l`y9Y2d>Nr+TM8B!Vy=g}b86ff`M&sZ=X7Kue(QVWuz9_o04RZ+YGrf}{&GuWex zQmuZB3VbP(2%%U+v@GHj1}T^tM5UfHc@%J~%g&v5?c_3V+gM&tcZkC~*}*+6!+D$Q69fr#zmR%3PwtF~h?3b4OOe!?>7pd}c5V0>pU;12h&gTxNop zsvu)9k2EX5ig6X54yKAi0XxDhT#1%L_^iw~6&fZJWRP>YL6Zgmc`U>aoDm+hW4Ikb zVB{K-AZjoX3Zp{klyQ;zoCg!5#$nk9x%N{Wdh6J6#A3%MKJulje}2vA49m8@@P!YH zx(s&!c7Pi7Lx+aySJ+X7sX|fJe$TN@9-0v9a>fv6bs1VTr){ZV_K2K^a)MweF~gmO zSVu&UlL|XnMnME)JSANegS||V{QO!*p50*;=t+m3LZBkbU0R!|B_SqSohlbi%%F8~z8XfY5cMWF_g4h`&jQMH&z zW(aGWuJB;;zz&{9tiZl2=s48S^y-`}oL+j1j8x~-fg0?089QO+Jv(>YcGz2zA71&> z_rK@EANkO~|M*8g*|lTOp;==)?%Dn6PkreJKlrI-8C$n*L5t!EM-g^h^@Ho){Nlg9 z{l*8F{=R+tZ@lrA?|=U%U-_DN2>y(UP>Q8+7VFxSKxjO0-Ux6LCNVeqZxv32&%*-_>>XIoj(!OvpDB+c?#>q1?lTUxf zWtTtYaj*EZ7eC^W=fO)+{isGwL|aHS1{+$2RaDjzN@#K@o4o3bA?$#Gtzn03ao!T+ z><6#0?G3c@T8U~%#A@TOgs_7*6h#$P;!z>rZo4~n?&UK7m}YKgdqn3R*4eE|+)CHI zW5;||zO@9#a?4G(wO890Uwk2#9f{b%zxEBUT;GJEx&b8^%#pSU#kDsT|NJ2XA{;k75m0Ith;p&hp@BTG*lMo@N}~&ZOTr0AYhMV$ zvSB2M9-1hJOA_L*J*rBqq%)~oI{W$hHaMcdBq}>p;=Pi)D;MotolEtt zoIVA>!JRvC8_kESpbWz@W;cvUqQzc!hgLJbc75|3-=mhB94p@YSA`-A2?rqr(gmMQ z(lp_2Ilx**cD!wB5D)_A>`}GaJ)vbvg(5&`ioP4?a)PoSuC?-_q+!LhoIk0NwWA0> zTuN$`2pN8N{_{kc)z3TXLfXfr-qlb#dcv-Ei2A9E77oFRc=2>BMmoujErhu)RSz(QMFI zPF5TV@3n+GfUSZJl9di&8~wBgYZ<14l=man^;6#oJ&+fKC-@t#@S_m20eJ(I;47dc zPBR$}LtTToGXTGW{K^3hl4U%EYnEpW&0{PCGgWvw$RZT87Y(YRLqe2;KIVQxVg&X8 zVhw1fnq^st0cclER4e|EgntUELXPJ9rwQBZ4H2^1-F~+_a9o#$>(az3EyYb~>7J5J zc&)C+c~qiqsJsX(lvi*~L5RWV0oZhXtLZ1$eR)`D*XIQdNQz2BYta`v;R1hk%#TqjVJZ0q;0X4>Qp~$jA?)izTfF|&= zD4-coh9uxt545-6vt#qz__5MnyI7)uRIQT|`=m(O7|YX%1RQ6<4pEz^SlpfRRls`S zP*p3JZo-5#G+ac1a9WuGdgHtyH{q&azX+Qs5n3o>Viritb57#Yi;reI;0ekbpMB-P+-DMwFXY(lFr+G$;qPAP<(J(i-n&GWA`u zAzJ@Ij($pGL?&_{RQ5V{9BJ6`<~O{f-ENP5{`+_T^|8lqSn|3)tqCB>f|TZ&N1P}I zl#H-L5ZN$7U`aVSb`xn@S2^WCx zq0u2ivn=KATx3l>JJvE(ux5dC_baNkJ2DNikOjm7G4SGC0(RVIfi1A^m}Zmb^v?Z| zv|v+^$~EjjoPw~G8+_c>oV$FXrB7_yefM2Z_-iNs*0i*^^7XG@@wv}^6}WK130p-` zI`XjtsQ3?W{-=+;`;)N4>ede`jW%NY?Fst#?(I9j{H1Sw^5dUhSy`Q&8XFsLjIiUK zfBg>&3yY)E&N=6tx4rExdCCDh#8OKU4`?hYL)c*xcF?A1Xe)X*3}p$o7x8R8&4kR0 z)QJi^Ds(!SAh_HW57DL7Y&1mE&8b@#N#pcMaa=g~;)3EQ>$rsr@5MC;wVGhCr6e81 z4xQ60iAqbMld-`0ON1TSAeU5FMh0HUa4EPmEQ*06aL_F@bpXFohdnMCVTS<$!T__{ zcko39?1)4b+A0VVKnIN&BJ3diOC!Fs*xk8n&)jr#lQeG@D})`5<5F{5EYF^K&Uwr2 z-i{r+s_fsJw%>6#^y&HMpO4Q+8g}r1@y0iz-qtsvK!QIUftyf#=p&6@pQ6uM4~gFP z`s|@+O0e4X4UE<|F#50GgaQV+zJZbSKpPnCUu6FE-?XoMVSIl7T4;LA>ASW~dPl_u zMlEGyb^G6)(wOD~;DeWY)w}=%RgoFo&HD4q#Mz~!l{7wKO-7z`6{uMcCg3zKA2C8uE_}@yG_eGUE77ptveWCEh8`E`Tk5?QRD^6tf{j5rjuC?$JgBdDK$IQB`j0Qh-K) zD>75Q4>lc2S(yeo)bD6s@F?NK$S?3;j_O*NZ%<4$_6~7Kn6^?kcfA{LFzDk8%m$@$-Z9E$gTd7$kslud@hp z0=_wAkF!Mc*hbxIHf%#P1e#nfftM+~o^u(11|$PF%<1JI-5CiPL-+#n#$_bPO{;Sk ztg;a86oRP2;w)o7{m+{h_R|p2jmP>=dPWCC8IXjpu!ODhRv0e?sGmCp7XmzB-eD0# zV>qc>6KsXmV|qQ5=(L;y$Pz&-{#Lzp$iGBgN2S13ebi(y^p{)BSG~fw`5Kx8RyD|7 z5jkd9C+}Cf9b#E5xRO;HoHkVs_wj?Bf?TIAWVb1DDhGuG&Y25*&#Vak zWWSWtZa8+{s(p4=T7D)ag_sslgESEy`5F;6G$Gpk(7UISDZFyXOF1A3H8L{sET!2a z;5lyUIqA@rY$EX-zZ5LB!~Y+9?*VAbS(Xb=?=!2f-S_F|BqyDOKp>D%RZ8ehRMab8 zDJn$}LAe)E`MFpBdr|cMD%g;!a*^H%olp`|NKS9NuDw^EKJ(4Y|GcyIIXMX^x%U@x zy#M~Pv(MUV&6@ej`##U}ezhlyDlNOt7-Ej5Rhc$sn#we!@030*acah=!_e(|14JDS zwN)Nx!JqO^K*odJaN|x$D;A-A@Tsm?bapU`lgeH%3ao}8Bmo0bg@(i})b>(tepu@J zoo(*8Fu7xGW%ED%r?*T#n3|jW&u@Aq0KtzVj6@BRHvb|Cwp20ZsQQj zO4L*qF-4K#hLDRg8VHJ!=S4||oX$ZOVip#0w@8M9zb-IO>3>J{4$Dce2>WU45s}#R zb2XR19UzLN&-wxL2&T$}Y6s0Hmhc?1$X8I47g`tQRX z?7rNAhlsqjAh3}!yt_WL`|78>@aik}oj!h|*BfkoGX+HNCpZ*Ike_^xP*jU^2%4NmplJ5uK{N`=%e8;;#{IO5={64JbZU1%aB*A90 z`N4mBk8Ro@3!a;Bhmi@0Qp5t$O357n-qORkjLT38a7R+afM2VBlozxdAO>tGxnq+0 zXNEyPZ0~Q$q8Jx6QiV4qUfiYbrmicN1(}x%5uSi1kMbCz#baJYvQ@!MvNY#YL(pUa zA#3S01WM#a#2q=YhQLwp5g81L zOVmkpU~l%u*W^=;?E{Rq4=@7TY#(5B0Ucm;=RN!@-^7+z#gveb(w<$#OCJ9XL6-hq z9AMNi<|oxWE%3>ggC0nAT7Twzrckz||a0;S;xR1s_&T%D|^XnV^jn3wd z#RWwnR-4=hxFK_jMBRvDQlKXJ9F0=aM3hI9u?i&o33UicDhEG=(@X1PPF^Y06J7${ zG@}bU>@s}(-%0>Gfs;(h4${C)$5Z;uIU9q!5wewzHZd(|vHobhw!YbDRN<(aXfuj- zI6_`oYBXxdBm#smjse196puY`YO1a&Dzb+}1rvl;v*B1)w>KzqmWVN|0=5Az2l)aj zY}rPu>7u?F^i0h(?>njIT!QTllR*SWOUBfVNGAuAV4z&mz7jbKwcZ(~R(07c_Epm) z^SwB18Flc5D#-)icN8KNK zVi=Luc=LxZ4jFOc>wYc2?2;rWJxKvJ>S~;gI@!idVsYO-6<<2FV);QFs+>s?P47;< zxnr3I+xdznq!Db9vveTQz3f~9X4w_8^#&3_b_qE&@W5v(uA+C*%FbSnMaKAh6{~eD zumT24!zVNom@W{Qs;Ql|f$OH#iUnV)lv~winV|4$Vf4LvRTued8cian-KeHJczwf?4@}|JN0e$GQisFMp8Wl4J>*ob3 z9pEXX7V~n>Ok)Z?VnmRtjJ5zU(&TYnBV8AzF^Yra$PTI#@4%wNEUsylVKlN7R9%G9 zDVz=7h>F7^Qv1yOvA`-k<_5=+AHa8HY6$}!5g5to#q)1>x zy4JG2btUZNT06*jCQ>Z=iGQ5E^{ce(u2xTe{uL)BeXH*YBsC+q9g$A-2T9@8-p4KeT<607B%UtUplxh)6jk51b4FbI zPFBBs$-3esu%6?ra`7O5LRB>znn+b62F-&wut*9%;`o3UHxn)fa}*ZBd(`RJi;xF0 zX#oTQ>ZG{{w3@FN`WTCtBfo(nBbJw@I zQcmMrAefG0APcDA?vu?Wb+DrT-YUnjfHDPkYFhq!kk64jc)tautTp`os&9y

    05t4edt5CxTCZS-pkz0ygQW` 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%0DJtaws)Se4P;&Yb!;AdxYE)o_Q89>N;Av&} z0}nEe6eX6FK%B(u_uu`@LkOcK4~o7f%x1~}GQnid2;4{Jhiwd95M{P5B)znL`tuk3 z45ZeC2}li`BXJ2k!j-KiSLsVPFlE~17C8l+FanDCTVZtuO+u5gMVV}41Y8gfHpS-m z!pV_o+hLQ&K6%89V6kCxso584H6~1ee)wQ9D0^4mvc!$-PV6G}$a>2n+`ctvKX{xg zqH=5-NFsfQM2=w8Y zf=HzxIkDGMYz1-G@T_MH2s>+~B}7VE1|W9Lh9TEVIzLsTcA(oEp=4Vf6K=V^x*BvU zX;whrBoJYSyiznrDm$S>A7sphoT<6ZcwlkLN%zMwSi;`GOH_qo!bv2#$eJX4l4S8l zFB>obN*8820h`+WGV512i{l;+iZV{-`)WVZ68=D7ognbN+rj^0(7WgCpXOt4*B7|Z zb17F3mg1R-6tj%t@8NMUAyY?QRFd6JIX{HNp-QTxzo2~1!`hg-2$yqz8Gm45;dSV@ zHAj&8hrSm_i_V@;$s&I}p6__z7G3VV9YmJH2BWVJ)w&+!WNRPq2ehYq-nc~lJ2yCN z?Ma#3a9mlR<6tu5IK#x4q;54o&AROHE4yk(G71*}{3PBqbxouATBq;f6T?5Q^OUgF zxHC_pS#3s}ZYwb2#cIpkcA@8p-a<$BVTSj~UCCM;Lv-b#ycEU{cB7Q8PytZFO2P6i zU}Q8jH%~2QLE(~uEFb^nKWE0pi#Ab`*{DM5r*sl6*ZM=M@(e+ZwxUDk7650v8h1-9 zWFQRq4sKi9q(+_yyH;+Lgp`NF4i8gOPXHQ9EjjN=FC_VgErt^s!GwT5(nS^4;zu~~ z??@-(s2NIaS{~_Tm{WfBt z$D!+b(tE-&sR;Hyr*Vo(T$;;7Ykx?eViIr;R=Yah5F8~| znQp^;4b2>4jbP>N-gZ{iMT~)inN=#CLgiT~ERqW9bGD$eSr71Fj|yH;O2$xQ@L}FZ zEeF&zcgT2;)1v{ER@wcea`M41L2MKBEnpz_g+7vdKbgOdxayi4pL=eocb-0WI=`LO z2)>=wZa?!3{}Pzy(zw?m)cvdxGOb#qF5i-684qA?nGg zk^NJO$EWSn6!{i=e5vvhZYENlx3s6RUe)qXsC*f+eT#Fx5aU5h8X-LF4n66GxIC;v zwU@WW^X&^J9CGk8%!avEI6&qptQ{l6spY%RSUK}80tmc(vqDZ~Sywp%LJS6lDk%UsNb?%geTEbpJfGXfpih&UJq zg!vQ@6iYA$Xdw@Adaf-Dj`9A40DS zN_Tc&uvCF*q#60V=ppABU1yS>AJ-nQKF764T6$^kks}wM-5Nxu^w6k6j>+zUi|we4 zp!5|Q((Hf*3x&m02sp}xX4Ayvz1Lx&o_&qIKBzQDlgpybzFn)x*+I+{BGY_QYx7D+JT6MC`4G(%z%!I{EcR-Q-K=TVIvN}tY}EImPRQ$Pwdd~;di?Y> zV|89`&(|k(REdgPK8(9GrKkCL?-k*vKuqRYAHPgLKD487oK* zu4p#H?{5K6sRu=@v*k0z52*Y^R-%-DfljM)ZiQOYwk%#i@gRO8YAX*}5%ZK{nq`v^ z6-Z*SEINI^Aa#u_^r3y=K$O|xZgrw;vI;k)EuE%w9?0IT@BJZuQd+~5LORZt)5;b? z6^SG8@mlQU>08)h6s|!ps@RNyN~MN|ieBEZfOtg`XDu1)z(2UjUfq#ew6$k4MPOml z3ZX1kk4<3GByWS|F|Ve4=UY>dh>&FfODPp9M#z4iWnnw=1XS=q6h>sgtrgoT%n{L#;3qA zU;ZNc%6WWFDKbe$?+1a81f2g5C%hV_GTJ{WQkj<^Ns1K`A7CmtS}Zm5`HKq?v)GWj zSWqUqzpuA)_LKZ}1FK#$C>ckfL#1hL@u!2cT>24a21g;~+#W&K8TzSnoPPf=0JT6$ zzv-g3(c&pxTF7-fem9Zhlt8hjXYGI{l4yKls9LR7I(qOtw6s_>DNKP4Q?D;MVKf%% z?aGhEa`6VO0ti#%Wm z6PB7>#pS-mdt4N_)2ZjT^IZ^iP!r!p>~On1#j=hsHi}VTEu3YV##e^^k77qY2+cWR zc5s>d&p-9k3m2|hvo=04`b+=mJ#T-<8!ur8Y~@s&uw%pO3`qC~gXS@VtX%~ix&A|P zT9fM+4p3JYA|qMS7Uq{F5$>0QCKNwE^|DaY9(hn)Sk1qYS+(SqKOGStB&J7o#=~t4 zr<1fN4{Oab-|nAzS^6q{$2+A$jWv#vg^t~ta+w>^22(}FC}@T%Ga!+w!M}1$py`yf z{y+%jUQ@p~_zg)EePR1u_SAJi(0mXfHD%qDRC+T;Nn`8vo@Etf8Nq*!9XubzId57#as_xVO@Q-`;Jh*S)(~FBslM|DB_B>oD7R%LwNOLDCmdae48=4S7 zBY+(WZP!pR4N24y{spPDjBH%d_0m^dciBq*Ws(%+n2v7Z3MQ-<6{G<>;BF-e z&fgB*Fo`N4*sVj%nP=sVTycugitG_Kd|eo~cxTzu*E>ErdQm6r$W@5engCKMz`lqb zaYmg-^G+ae)?@)(aTPn<7&wBHBrcPwhl3Pu6+56~agqiB4rERY9mr?Gf{!!KkK(TH zGg_HNyV@ZVgeRnRtT}d=mX13%RzMF>n<521gb_J*(8k^5h$ww3VH;sbUI%@BChUNX zsglHVA}lA$V#n>h@4f%!6QB5OegcUsOTYi2|M9+`|MzRy@q)q*_P}rd?c+0p_n+U` z4f7mD7v-aW$O;u-LA1_GJrCL%10$$sI2AL2`Xip+$A0}oQt#pXklS2Bw;NDG@yY{GfvS*jh&jbpD~MvITjCQd!myTZu4FVnBp1}q zkp}C9d`)4Kg-nsCDp7A&c7A~+gFfP|<3v2%Ov+j`rYmA5r7ZA#kSb~?gtgHAOOxwR zn4X=?&8>h3504iFqD}uYN$qCmkKUDC-RVQS15IDA?0T8F`F0-Q@GU|c8128$f9xMG zZDw~#cv(}|@5ruuTfOBbdZfzQfN4aXhWn{sUp)iRxc*wP+n_m6+wT-OURyX>{QBRD zR}YxS#hQEV%Ldoq05=!-tX}VEB_UJ%uIHP&!uy&S8@AV#dAU-tL@5K}C9{vGAv|f4 zsx@KwS_bmoKAY4}z~m$@DQ(Vb)ZKwxZ$1tWk(M>H~(%09(2kzm{EPyX%=FdfL}{-0-5nNN=`g%2On zUU{=G&4tGwOD-JZVw!PtNrWH%&C|L%pw+5s^?Yx3(pbkrgh;S{3GPkNB4}1>OYvXc z%hnCGZkk3sIxuL3KIb{1X^Du%6p=QbbA7>}rQ95}33`~OvEy|NtsvBo#%{e;-EQzY z7gJs;yipPfl+K+?dvOn*z?2LI`vGrYl&cFA-;KSBZi-Cqgk4?GXka0+XnB8M4jw(Z zYx|ZQiMjm^FG%ApMHL7SG+A{YSIeQ}+57%G75eROiz!^7Ctd|ef)1`XuJ zt}IFc=rHn;#lOA=_^6XMAlH>%4md ziiX?@zDl@(oe}t=ke*v&pLwtn1?FAXcJH{hg#zPdh!x$)?fHS%g!^G$EI z-Jv~69QT@Qt~q!9EUl7ry;^OsHZkoq78jQ0;Yv4c+Kd?H$Tyz;)d$|v^*{}?55M!V zk)Vj^xy z_2!W%?1h8ZD8(@^gzJzcN^ik=UM5|*N3zBvb2A4+08#ch(DXRYWLP7u^iP0&ZtTNj zfwDJXe~!GpV&`ktD5r@R(VB=nqUK5cMFfPGRFEO+fKe#Ch4L;q%VCSEqZ`j>-Z+MutS{IlUr%^BJ>5^(kZdomo;$KvE zT#Fc$QrIvCn+CTAVapj^|AecQ{F2}_Tm^MfDMUdmfjWu(#Gmi23~9r%sC0sc>N`>t z2d(oa>n4&F3Q9+wFtt*dwYkvF{08SWp`T4KW#_EQx-vzP#_~ zxv7n#8>XfyZYLW8gi{wrzZiLcCe1nzQ%@6(h8x%0?a@-9Xi6v*3W*Ztr^Rr0@G$+W zU>a}*l&0j{3%t?6BPXWT4P{c&3j{9;5Nj{H;)YTr=wXdyRH$ZH>5@D{{(h``9x`Y>defM6AMQU?EBr{_^s7%IPQ)+Z~37gdF$OTxee@U z^-wMsx`G?06IN!5A|=}ZcEC8`LFu-u*pa_?zwe+g^w1!Vp4cq*ji6N5 z`&M;j!APR7f5bd>|LPz3Zo|##z;vdUS#jvni8Q_E{N8x}xUzC_jh+BI*~G@L2`kf# z2X;hU;{=hV+SVA65TBf$Wgq!;)$`2P+}VBAov<~!8P5rR?1IeucEZVV!27b@G}=pY z=y|H)46b9+MeNACIq)JS)zT=HH7*z1Z3p3dwQN9naw8JG*vA*DB=^X{rlBvGl38yw zt$_g$s;_?jv)}XD*MRgs`NWgslaq%JAK9^E$GUZs=gwWQ?cVS}b<~iS+Wtbft7*Uv zZ~OMGGqbY^oRa64E4AwAgxg$RsxQOH*Kb&_@Y1nw9{7n5zq!$++_<0l;A2zv2^J@a z3~3b+EImnS0eK%gim|w<5)5zl(@5r38v103=kcHT)Zl#&7mm+Xl76rIw~@d3^+R`E zyOR3dIQAGIhx0+f`PLGD>Z#Q;5FEYrw^`K9l8_5pFbY1hGtp$$NTQE~S*o>8#E}!$ zwkg&yWdjjpicfJPKa=Jj5Y3U;SkK9*S_TrjiXAj}D5KmC#8mx)szYg8LAvNln@f%) zP^KC@mo};cb`S$(v{k;4uUT0YM!T7(vCIP`qGh)pWtkUQC0VBNu#79i)2E@AB+&H& z0+5teB%tjq$&`4)j%LfTyY|39QI=sglq1EC98FT3$SWxp>CnYTMQI$wp(*J&g3p8< zAli8ha^m>upZ(bn%=B}z<}yAp`nP}k$JgI*%^G&R(6B=|D*Vyf21c|7W^DtbwGE7D z3FF!ZM&Gp!jK*q(>CM^Hc4jZrqFvb6-h=Fkr{t;eH1WEZHlauY?Mq(|Kl!Q3-+fGb z`WazvytSZqQy_n@~)apVz20N$j97 zA_xm%q~OFhPcwHVnouZ&aDxJkekf2Fv@p&DMx)_bae$4i<0dz;bsM

    1<+Kwr=ZH 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⋙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

    tf$D=HWQm_qT*T_OFv13F39iL$EP%nPEc+Ec8 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 z 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 ## Introduction @@ -41,7 +41,7 @@ The `onebusaway-gtfs-transformer-cli` command-line application is a simple comma [GTFS](https://developers.google.com/transit/gtfs) feeds. ### Requirements - + * Java 17 or greater ### Getting the Application @@ -49,7 +49,7 @@ The `onebusaway-gtfs-transformer-cli` command-line application is a simple comma You can download the application from Maven Central: https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-transformer-cli/ Select the largest jar file from the version you would like to use, for example https://repo1.maven.org/maven2/org/onebusaway/onebusaway-gtfs-transformer-cli/2.0.0/onebusaway-gtfs-transformer-cli-2.0.0.jar - + ### Using the Application To run the application: @@ -61,20 +61,20 @@ java -jar onebusaway-gtfs-transformer-cli.jar [-args] input_gtfs_path ... output `input_gtfs_path` and `output_gtfs_path` can be either a directory containing a GTFS feed or a .zip file. _Note_: Transforming large GTFS feeds is processor and memory intensive. You'll likely need to increase the -max amount of memory allocated to Java with an option like `-Xmx1G` or greater. Adding the `-server` argument -if you are running the Oracle or OpenJDK can also increase performance. +max amount of memory allocated to Java with an option like `-Xmx1G` or greater. Adding the `-server` argument +if you are running the Oracle or OpenJDK can also increase performance. ### Arguments * `--transform=...` : specify a transformation to apply to the input GTFS feed (see syntax below) * `--agencyId=id` : specify a default agency id for the input GTFS feed * `--overwriteDuplicates` : specify that duplicate GTFS entities should overwrite each other when read - - + + ### Transform Syntax Transforms are specified as snippets of example. A simple example to remove a stop might look like: - + ``` {"op":"remove","match":{"file":"stops.txt","stop_name":"Stop NameP"}} ``` @@ -89,17 +89,17 @@ You can have multiple `--transform` arguments to specify multiple transformation transformations that you wish to apply, it can be easier to put them in a file, with a JSON snippet per line. Then specify the file on the command-line: -``` +``` --transform=path/to/local-file ``` You can even specify a URL where the transformations will be read: - -``` + +``` --transform=http://server/path ``` -### Matching +### Matching We provide a number of configurable transformations out-of-the-box that can do simple operations like adding, updating, retaining, and removing GTFS entities. Many of the transforms accept a "`match`" term that controls how the @@ -183,7 +183,7 @@ collection. You can use the calendar collection matches, for example, to retain a calendar, including all `calendar.txt`, `calendar_dates.txt`, and `trip.txt` entries that reference the specified `service_id` value. This convenient -short-hand is easier than writing the equivalent expression using references to the three file types separately. +short-hand is easier than writing the equivalent expression using references to the three file types separately. ### Types of Transforms @@ -206,7 +206,7 @@ You can update arbitrary fields of a GTFS entity. Normally, update values are used as-is. However, we support a number of special update operations: - + #### Find/Replace ``` @@ -220,16 +220,16 @@ following example: ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"s/North/N/"}} ``` - + Here, a trip with a headsign of `North Seattle` will be updated to `N Seattle`. - -#### Path Expressions + +#### Path Expressions By using `path(...)` syntax in the update value, the expression will be treated as a compound Java bean properties path expression. This path expression will be evaluated against the target entity to produce the update value. Consider the following example: - + ``` {"op":"update", "match":{"file":"trips.txt"}, "update":{"trip_short_name":"path(route.longName)"}} ``` @@ -240,10 +240,10 @@ associated route. #### Retain an Entity -We also provide a powerful mechanism for selecting just a sub-set of a feed. -You can apply retain operations to entities you wish to keep and all the supporting entities referenced +We also provide a powerful mechanism for selecting just a sub-set of a feed. +You can apply retain operations to entities you wish to keep and all the supporting entities referenced by the retained entity will be retained as well. Unreferenced entities will be pruned. - + In the following example, only route B15 will be retained, along with all the stops, trips, stop times, shapes, and agencies linked to directly by that route. ``` @@ -281,7 +281,7 @@ Retain Up From Polygon is an operation that filters GTFS input data based on a s This strategy applies two main functions: * **Retain Function**: retains **up** all stops, trips, and routes that are located inside the defined polygon. - + The algorithm starts by applying retain up to each entity, traversing the entity dependency tree. Starting from the stop, retain up is applied to the stop_times referencing this stop, then to the trips, and so on. Once the base of the entity tree is reached, it automatically applies retain **down** to all the traversed entities. Therefore, all the trips of the route and then all the stop_times of each trip will be tagged as **retain**. @@ -313,11 +313,11 @@ Only valid stop_times within the polygon are retained, maintaining the integrity ``` {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.TrimTripFromPolygon","polygon":"POLYGON ((-123.0 37.0, -123.0 38.0, -122.0 38.0, -122.0 37.0, -123.0 37.0))"} ``` - + #### Trim a Trip You can remove stop times from the beginning or end of a trip using the "trim_trip" operation. Example: - + ``` {"op":"trim_trip", "match":{"file":"trips.txt", "route_id":"R10"}, "from_stop_id":"138S"} ``` @@ -338,7 +338,7 @@ Or both: #### Generate Stop Times You can generate stop time entries for a trip. Example: - + ``` {"op":"stop_times_factory", "trip_id":"TRIP01", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["S01", "S02", "S03"]} ``` @@ -393,18 +393,18 @@ By default, it deletes entries from both the calendar.txt and calendar_dates.txt With the remove_today attribute added to the JSON transformer snippet, users can control whether entries in calendar or calendar_dates that are valid for today are included or excluded in the GTFS output. * If remove_today is set to true, the transformer will remove entries for the current date. - + ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":true} ``` - + * If remove_today is set to false or not specified, the transformer will retain the calendar entries for the current date. - + ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements", "remove_today":false} ``` -Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. +Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. Without this retain operation, the `trips.txt` file will contain trips with non-existent calendar dates, leading to invalid data. @@ -431,10 +431,10 @@ This operation truncates calendar and calendar date entries based on the configu - `Calendar.MONTH` = 2 (default) - `Calendar.DAY_OF_MONTH` = 5 - `Calendar.DAY_OF_YEAR` = 6 - + * calendar_amount: Specifies the number of units to truncate entries. The value is an integer representing the amount (default = 1). - + Both `calendar_field` and `calendar_amount` must be provided as integers in the JSON transformer. If these parameters are not specified, the default behavior is truncation by 1 month. @@ -442,7 +442,7 @@ If these parameters are not specified, the default behavior is truncation by 1 m Example : Truncate calendar and calendar dates to the next 21 days: - + ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} ``` @@ -453,7 +453,7 @@ Truncate entries to the next 3 months: {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":2,"calendar_amount":3} ``` -Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. +Additionally, after truncating the calendar entries, it is recommended to use a **retain operation** to ensure that only trips with valid calendar dates are retained. Without this retain operation, the `trips.txt` file will contain trips with non-existent calendar dates, leading to invalid data. @@ -461,7 +461,7 @@ Without this retain operation, the `trips.txt` file will contain trips with non- {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} {"op":"retain", "match":{"file":"calendar_dates.txt"}, "retainBlocks":false} ``` - + #### Merge Trips and Simplify Calendar Entries Some agencies model their transit schedule favoring multiple entries in calendar_dates.txt as opposed to a more concise @@ -477,7 +477,7 @@ calendar entries to match. To run it, apply the following transform: ``` The transform takes additional optional arguments to control its behavior: - + * min_number_of_weeks_for_calendar_entry - how many weeks does a service id need to span before it gets its own entry in calendar.txt (default=3) @@ -491,14 +491,14 @@ The transform takes additional optional arguments to control its behavior: Frequency is defined as how often the target day of the week occurs vs the count for day of the week appearing MOST frequently for the service id (default=0.5) - + * undo_google_transit_data_feed_merge_tool - set to true to indicate that merged trip ids, as produced by the [GoogleTransitDataFeedMergeTool](http://code.google.com/p/googletransitdatafeed/wiki/Merge), should be un-mangled where possible. Merged trip ids will often have the form `OriginalTripId_merged_1234567`. We attempt to set the trip id back to `OrginalTripId` where appropriate. - - + + #### Shift Negative Stop Times Some agencies have trips that they model as starting BEFORE midnight on a given service date. For these agencies, it @@ -514,21 +514,21 @@ To run it, apply the following transform: ``` {"op":"shift_negative_stop_times"} ``` - + _A note on negative stop times:_ When writing negative stop times, the negative value ONLY applies to the hour portion of the time. Here are a few examples: - + * "-01:45:00" => "23:45:00" on the previous day - - * "-05:13:32" => "19:13:32" on the previous day + + * "-05:13:32" => "19:13:32" on the previous day * Remove non-revenue stops - Stop_times which do not allow pick up or drop off are also known as non-revenue stops. Some GTFS consumers display - these stops as if they were stops that passengers can use, at which point it is helpful to remove them. - + Stop_times which do not allow pick up or drop off are also known as non-revenue stops. Some GTFS consumers display + these stops as if they were stops that passengers can use, at which point it is helpful to remove them. + To remove them, apply the following transform: - + ``` {"op":"remove_non_revenue_stops"} ``` @@ -597,7 +597,7 @@ to support those routes. Consider an existing feed with a number of routes and stops. We can add an entirely new route, with trips and stop-times and frequency-based service, using the transform. This can be handy to add temporary service to an existing feed. - + ``` {"op":"add", "obj":{"file":"routes.txt", "route_id":"r0", "route_long_name":"Temporary Shuttle", "route_type":3}} @@ -613,23 +613,27 @@ and frequency-based service, using the transform. This can be handy to add temp {"op":"stop_times_factory", "trip_id":"t1", "start_time":"06:00:00", "end_time":"06:20:00", "stop_ids":["s3", "s2", "s1", "s0"]} ``` -### Clean National GTFS for Regional Integration and Consistency +### Clip National GTFS for Regional Integration and Consistency -To prepare for the integration of the national GTFS with our regional GTFS, several transformations have benn applied to the national GTFS in order to clean up and adapt the data to regional requirements. Below is an overview of the operations carried out: +This section of the document describes how to reduce a large GTFS to a smaller area. Several transformations can be applied to a national GTFS to clean it up and adjust the data to a regional area in order to get ready for the integration with another regional GTFS. Below is an overview of the operations carried out: * Removing Inactive Calendars and Dates. * Truncating Calendars and Dates to 21 days. - * Retaining Data Within a Specific Geographic Area: a smaller geographic area was used for retaining only the entities within our area of interest. - * Trimming Stop Times Outside a Specific Geographic Area: a larger polygon was used to ensure that only the relevant stops_times within a wider region are retained. + * Retaining Data Within a Specific Geographic Area: a small geographic area is used for retaining only the entities within our area of interest. All routes and trips that do not pass through this area will therefore be eliminated. + * Trimming Stop Times Outside a Specific Geographic Area: a larger polygon is used to ensure that only the relevant stops_times within a wider region are retained. That means that all trips that go outside the area are truncated. + * Clean up entities that are no longer referenced by any trips. + +RetainUpFromPolygon and TrimTripFromPolygon together will clip the GTFS data to a small area and allow some Origin/Destination transit to nearby cities. ``` {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.RemoveOldCalendarStatements"} - {"op":"transform", "class":"org.onebusaway.gtfs_transformer.impl.TruncateNewCalendarStatements","calendar_field":6,"calendar_amount":21} - {"op":"retain", "match":{"file":"calendar_dates.txt"}, "retainBlocks":false} {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.RetainUpFromPolygon","polygon":"MULTIPOLYGON (((1.2 43.7, 1.55 43.7, 1.55 43.4, 1.2 43.4, 1.2 43.7)))"} {"op":"transform","class":"org.onebusaway.gtfs_transformer.impl.TrimTripFromPolygon","polygon":"MULTIPOLYGON (((1.0 44.2, 2.2 44.2, 2.2 43.3, 1.0 43.3, 1.0 44.2)))"} -``` \ No newline at end of file +{"op":"retain", "match":{"file":"trips.txt"}, "retainBlocks":false} +``` + +![RetainUpFromPolygon and TrimTripFromPolygon](onebusaway-gtfs-transformer-cli-sample1.png "RetainUpFromPolygon and TrimTripFromPolygon") \ No newline at end of file From 71f79c282805d471f6a998c0b61719d86c4fa00a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 19 Dec 2024 10:14:58 +0100 Subject: [PATCH 223/234] [maven-release-plugin] prepare release v4.5.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index f18277f8b..12c8e91b7 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index c1e07c9e3..0438db9ce 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 8f1919df5..6de729110 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index c3dc8b806..40f007262 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 623f125a8..2d2611e29 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.4.1-SNAPSHOT + 4.5.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index a32413b72..7344256a5 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.4.1-SNAPSHOT + 4.5.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index aea0799e3..e21a9a9e7 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 53ef4d7c5..953765b83 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index f80ddaeff..e601a4aa5 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 ../pom.xml diff --git a/pom.xml b/pom.xml index bc27412dc..323d14c74 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.4.1-SNAPSHOT + 4.5.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v4.5.0 From 04746be5188482b328a22757a2b910275d52ae54 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 19 Dec 2024 10:14:59 +0100 Subject: [PATCH 224/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 12c8e91b7..00693e922 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 0438db9ce..923646c06 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 6de729110..e539ea62d 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 40f007262..7cf559bab 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 2d2611e29..b75d24501 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.5.0 + 4.5.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 7344256a5..660d5d3ca 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.5.0 + 4.5.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index e21a9a9e7..db528f88a 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index 953765b83..e05a182f0 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index e601a4aa5..7f18e31a9 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 323d14c74..343b3ff61 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.0 + 4.5.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v4.5.0 + HEAD From 832a5ce74b88a72b85284f368550798149abc267 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 19 Dec 2024 14:22:31 +0200 Subject: [PATCH 225/234] Add documentation about docker images. --- docs/index.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/index.md b/docs/index.md index f30d33572..07dac4987 100644 --- a/docs/index.md +++ b/docs/index.md @@ -45,6 +45,21 @@ ``` +## Docker images + +There are automatically generated docker images available at https://registry.hub.docker.com/u/opentransitsoftwarefoundation. +Contributions to image-specific documentation are welcome. + +### `onebusaway-gtfs-transformer-cli` + +See [the full documentation](./onebusaway-gtfs-transformer-cli.md) for more configuration options. + +For example, assuming that all the following files are in the `/path/to/local/data/directory` directory, to run the `remove-matching-route.rule` rule against `gtfs-data.zip` to generate `gtfs-data-out.zip` you can use: +```bash +docker run -v /path/to/local/data/directory:/data --rm opentransitsoftwarefoundation/onebusaway-gtfs-transformer-cli:4.4.0 --transform=/data/remove-matching-route.rule /data/gtfs-data.zip /data/gtfs-data-out.zip +``` +The `gtfs-data-out.zip` file will be in the `/path/to/local/data/directory` directory. + ## Example Code * Basic Reading From 0a4daf6f1a858254ddd6a67ce278f1a91e447424 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:20:26 +0000 Subject: [PATCH 226/234] Update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.11.2 (#315) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 343b3ff61..1023a8b02 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.1 + 3.11.2 none false From a761a276efd4ad118c61271c8208d9b9f10b7fa2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 22:15:42 +0000 Subject: [PATCH 227/234] Update dependency org.json:json to v20241224 --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index e05a182f0..ef48d944b 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -38,7 +38,7 @@ org.json json - 20240303 + 20241224 org.junit.jupiter From 0fd2451430376fe926e8ad6a981e95956b42b615 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 11:22:56 +0100 Subject: [PATCH 228/234] Remove wkt from area --- .../src/main/java/org/onebusaway/gtfs/model/Area.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java index b09828c10..2af972bda 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java @@ -30,9 +30,6 @@ public final class Area extends IdentityBean { @CsvField(name="area_name", optional = true) private String name; - @CsvField(name="wkt", optional = true) - private String wkt; - public Area() { } @@ -40,7 +37,6 @@ public Area() { public Area(Area a) { this.id = a.id; this.name = a.name; - this.wkt = a.wkt; } @@ -60,11 +56,4 @@ public void setId(AgencyAndId areaId) { public void setName(String name) { this.name = name; } - public String getWkt() { - return wkt; - } - - public void setWkt(String wkt) { - this.wkt = wkt; - } } From 1c62e6be9f82ce320368ef353cec98e95cc8206b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 11:35:00 +0100 Subject: [PATCH 229/234] Use pattern variable --- .../java/org/onebusaway/gtfs/model/Area.java | 22 ++++++- .../gtfs/serialization/GtfsReader.java | 60 +++++++------------ 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java index 2af972bda..52c352788 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java @@ -15,6 +15,10 @@ */ package org.onebusaway.gtfs.model; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.csv_entities.schema.annotations.CsvFields; import org.onebusaway.gtfs.serialization.mappings.DefaultAgencyIdFieldMappingFactory; @@ -22,14 +26,18 @@ @CsvFields(filename = "areas.txt", required = false) public final class Area extends IdentityBean { - private static final long serialVersionUID = 1L; - @CsvField(name="area_id", mapping = DefaultAgencyIdFieldMappingFactory.class) private AgencyAndId id; @CsvField(name="area_name", optional = true) private String name; + // we use a List, not Set to keep the insertion order. by definition these stops don't have an + // order but it's nice for clients to not randomly change it. + @CsvField(ignore = true) + private List stops = new ArrayList<>(); + + public Area() { } @@ -39,7 +47,6 @@ public Area(Area a) { this.name = a.name; } - public String getAreaId() { return id.getId(); } @@ -48,6 +55,15 @@ public AgencyAndId getId() { return id; } + private void setStops(Collection stops) { + this.stops = List.copyOf(stops); + } + + public void addStop(Stop stop) { + this.stops.add(stop); + } + + public void setId(AgencyAndId areaId) { this.id = areaId; } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index a3a09b8a7..6b966b59a 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -323,55 +323,39 @@ public void handleEntity(Object entity) { return; _agencies.add((Agency) entity); - } else if (entity instanceof BookingRule) { - BookingRule bookingRule = (BookingRule) entity; + } else if (entity instanceof final BookingRule bookingRule) { registerAgencyId(BookingRule.class, bookingRule.getId()); - } else if (entity instanceof Pathway) { - Pathway pathway = (Pathway) entity; + } else if (entity instanceof final Pathway pathway) { registerAgencyId(Pathway.class, pathway.getId()); - } else if (entity instanceof Level) { - Level level = (Level) entity; + } else if (entity instanceof final Level level) { registerAgencyId(Level.class, level.getId()); - } else if (entity instanceof Route) { - Route route = (Route) entity; + } else if (entity instanceof final Route route) { registerAgencyId(Route.class, route.getId()); - } else if (entity instanceof Trip) { - Trip trip = (Trip) entity; + } else if (entity instanceof final Trip trip) { registerAgencyId(Trip.class, trip.getId()); - } else if (entity instanceof Stop) { - Stop stop = (Stop) entity; + } else if (entity instanceof final Stop stop) { registerAgencyId(Stop.class, stop.getId()); - } else if (entity instanceof FareProduct) { - FareProduct product = (FareProduct) entity; + } else if (entity instanceof final FareProduct product) { registerAgencyId(FareProduct.class, product.getId()); - } else if (entity instanceof FareMedium) { - FareMedium medium = (FareMedium) entity; + } else if (entity instanceof final FareMedium medium) { registerAgencyId(FareMedium.class, medium.getId()); - } else if (entity instanceof RiderCategory) { - RiderCategory category = (RiderCategory) entity; + } else if (entity instanceof final RiderCategory category) { registerAgencyId(RiderCategory.class, category.getId()); - } else if (entity instanceof FareAttribute) { - FareAttribute fare = (FareAttribute) entity; + } else if (entity instanceof final FareAttribute fare) { registerAgencyId(FareAttribute.class, fare.getId()); - } else if (entity instanceof Note) { - Note note = (Note) entity; + } else if (entity instanceof final Note note) { registerAgencyId(Note.class, note.getId()); - } else if (entity instanceof Area) { - Area area = (Area) entity; + } else if (entity instanceof final Area area) { registerAgencyId(Area.class, area.getId()); - } else if (entity instanceof Location) { - Location location = (Location) entity; + } else if (entity instanceof final Location location) { registerAgencyId(Location.class, location.getId()); - } else if (entity instanceof LocationGroup) { - var group = (LocationGroup) entity; + } else if (entity instanceof final LocationGroup group) { registerAgencyId(LocationGroup.class, group.getId()); - } else if (entity instanceof LocationGroupElement) { - LocationGroupElement locationGroupElement = (LocationGroupElement) entity; + } else if (entity instanceof final LocationGroupElement locationGroupElement) { LocationGroup locationGroup = _entityStore.getEntityForId(LocationGroup.class, locationGroupElement.getLocationGroupId()); locationGroup.addLocation(locationGroupElement.getStop()); - } else if (entity instanceof StopAreaElement) { - var stopAreaElement = (StopAreaElement) entity; + } else if (entity instanceof final StopAreaElement stopAreaElement) { var stopArea = _entityStore.getEntityForId(StopArea.class, stopAreaElement.getArea().getId()); if (stopArea == null) { stopArea = new StopArea(); @@ -379,17 +363,13 @@ public void handleEntity(Object entity) { _entityStore.saveEntity(stopArea); } stopArea.addLocation(stopAreaElement.getStopLocation()); - } else if (entity instanceof Vehicle) { - Vehicle vehicle = (Vehicle) entity; + } else if (entity instanceof final Vehicle vehicle) { registerAgencyId(Vehicle.class, vehicle.getId()); - } else if (entity instanceof Facility){ - Facility facility = (Facility) entity; + } else if (entity instanceof final Facility facility){ registerAgencyId(Facility.class, facility.getId()); - } else if (entity instanceof FacilityPropertyDefinition){ - FacilityPropertyDefinition facilityPropertyDefinition = (FacilityPropertyDefinition) entity; + } else if (entity instanceof final FacilityPropertyDefinition facilityPropertyDefinition){ registerAgencyId(FacilityPropertyDefinition.class, facilityPropertyDefinition.getId()); - } else if (entity instanceof Icon){ - Icon icon = (Icon) entity; + } else if (entity instanceof final Icon icon){ registerAgencyId(Icon.class, icon.getId()); } From 73e9a263d4f770a60847a2b5307d98dcd4921325 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 12:33:54 +0100 Subject: [PATCH 230/234] Remove StopArea --- .../impl/HibernateGtfsRelationalDaoImpl.java | 13 ++-- .../org/onebusaway/gtfs/impl/GtfsDaoImpl.java | 5 -- .../gtfs/impl/GtfsDataServiceImpl.java | 5 -- .../java/org/onebusaway/gtfs/model/Area.java | 3 + .../org/onebusaway/gtfs/model/StopArea.java | 63 ------------------- .../gtfs/model/StopAreaElement.java | 14 ++--- .../gtfs/serialization/GtfsReader.java | 14 ++--- .../StopLocationFieldMappingImpl.java | 9 ++- .../org/onebusaway/gtfs/services/GtfsDao.java | 2 - .../gtfs/serialization/FaresV2ReaderTest.java | 28 +++++++++ .../FlexDropOffSpellingTest.java | 7 +-- .../gtfs/serialization/FlexReaderTest.java | 51 --------------- .../piercetransit-stop-areas-flex/areas.txt | 1 - .../stop_areas.txt | 3 - 14 files changed, 53 insertions(+), 165 deletions(-) delete mode 100644 onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopArea.java diff --git a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java index 67bcdfbb2..db306a085 100644 --- a/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java +++ b/onebusaway-gtfs-hibernate/src/main/java/org/onebusaway/gtfs/impl/HibernateGtfsRelationalDaoImpl.java @@ -312,11 +312,11 @@ public Collection getAllLocationGroupElements() { @Override public Collection getAllStopAreaElements() { - Collection groups = _ops.find("FROM StopArea"); - return groups.stream().flatMap(group -> group.getLocations().stream().map(stopLocation -> { + Collection areas = _ops.find("FROM StopArea"); + return areas.stream().flatMap(area -> area.getStops().stream().map(stopLocation -> { var stopAreaElement = new StopAreaElement(); - stopAreaElement.setId(group.getId()); - stopAreaElement.setStopLocation(stopLocation); + stopAreaElement.setId(area.getId()); + stopAreaElement.setStop(stopLocation); return stopAreaElement; })).collect(Collectors.toList()); } @@ -325,10 +325,7 @@ public Collection getAllStopAreaElements() { public Collection getAllLocationGroups() { return _ops.find("FROM LocationGroup"); } - @Override - public Collection getAllStopAreas() { - return _ops.find("FROM StopArea"); - } + @Override public Collection getAllLocations() { return _ops.find("FROM Location"); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java index f814ee124..5ae18606e 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDaoImpl.java @@ -313,11 +313,6 @@ public Collection getAllStopAreaElements() { return getAllEntitiesForType(StopAreaElement.class); } - @Override - public Collection getAllStopAreas() { - return getAllEntitiesForType(StopArea.class); - } - public Collection getAllLocations() { return getAllEntitiesForType(Location.class); } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java index d67aa9df4..680b07b9e 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/impl/GtfsDataServiceImpl.java @@ -378,11 +378,6 @@ public Collection getAllStopAreaElements() { return _dao.getAllStopAreaElements(); } - @Override - public Collection getAllStopAreas() { - return _dao.getAllStopAreas(); - } - @Override public Collection getAllLocationGroups() { return _dao.getAllLocationGroups(); diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java index 52c352788..f0c304394 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/Area.java @@ -72,4 +72,7 @@ public void setId(AgencyAndId areaId) { public void setName(String name) { this.name = name; } + public Collection getStops() { + return List.copyOf(stops); + } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopArea.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopArea.java deleted file mode 100644 index 2d2d74a64..000000000 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopArea.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2023 Leonard Ehrenfried - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onebusaway.gtfs.model; - -import java.util.HashSet; -import java.util.Set; - -public class StopArea extends IdentityBean implements StopLocation { - - private static final long serialVersionUID = 1L; - - private Area area; - - private Set stops = new HashSet<>(); - - @Override - public AgencyAndId getId() { - return area.getId(); - } - - @Override - public void setId(AgencyAndId id) { - } - - public void setArea(Area area) { - this.area = area; - } - - public Set getLocations() { - return stops; - } - - private void setLocations(Set stops) { - this.stops = stops; - } - - public void addLocation(StopLocation location) { - this.stops.add(location); - } - - public String getName() { - return area.getName(); - } - - @Override - public void setName(String name) { - - } - -} diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopAreaElement.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopAreaElement.java index b0ee1cf61..2ea7402a2 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopAreaElement.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/model/StopAreaElement.java @@ -25,8 +25,8 @@ public final class StopAreaElement extends IdentityBean { @CsvField(name = "area_id", mapping = EntityFieldMappingFactory.class) private Area area; - @CsvField(name = "stop_id", mapping = StopLocationFieldMappingFactory.class) - private StopLocation stopLocation; + @CsvField(name = "stop_id", mapping = EntityFieldMappingFactory.class) + private Stop stop; public void setArea(Area area) { this.area = area; @@ -38,7 +38,7 @@ public Area getArea() { @Override public AgencyAndId getId() { - return new AgencyAndId(getArea().getId().getAgencyId(), String.format("%s_%s", area.getId().getId(), stopLocation.getId().getId())); + return new AgencyAndId(getArea().getId().getAgencyId(), String.format("%s_%s", area.getId().getId(), stop.getId().getId())); } @Override @@ -46,11 +46,11 @@ public void setId(AgencyAndId id) { } - public void setStopLocation(StopLocation stopLocation) { - this.stopLocation = stopLocation; + public void setStop(Stop stop) { + this.stop = stop; } - public StopLocation getStopLocation() { - return stopLocation; + public Stop getStop() { + return stop; } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java index 6b966b59a..47543b22b 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/GtfsReader.java @@ -347,22 +347,16 @@ public void handleEntity(Object entity) { registerAgencyId(Note.class, note.getId()); } else if (entity instanceof final Area area) { registerAgencyId(Area.class, area.getId()); - } else if (entity instanceof final Location location) { registerAgencyId(Location.class, location.getId()); } else if (entity instanceof final LocationGroup group) { registerAgencyId(LocationGroup.class, group.getId()); } else if (entity instanceof final LocationGroupElement locationGroupElement) { - LocationGroup locationGroup = _entityStore.getEntityForId(LocationGroup.class, locationGroupElement.getLocationGroupId()); + var locationGroup = _entityStore.getEntityForId(LocationGroup.class, locationGroupElement.getLocationGroupId()); locationGroup.addLocation(locationGroupElement.getStop()); } else if (entity instanceof final StopAreaElement stopAreaElement) { - var stopArea = _entityStore.getEntityForId(StopArea.class, stopAreaElement.getArea().getId()); - if (stopArea == null) { - stopArea = new StopArea(); - stopArea.setArea(stopAreaElement.getArea()); - _entityStore.saveEntity(stopArea); - } - stopArea.addLocation(stopAreaElement.getStopLocation()); + var area = _entityStore.getEntityForId(Area.class, stopAreaElement.getArea().getId()); + area.addStop(stopAreaElement.getStop()); } else if (entity instanceof final Vehicle vehicle) { registerAgencyId(Vehicle.class, vehicle.getId()); } else if (entity instanceof final Facility facility){ @@ -384,7 +378,7 @@ private void registerAgencyId(Class entityType, AgencyAndId id) { Map agencyIdsByEntityId = _agencyIdsByEntityClassAndId.get(entityType); if (agencyIdsByEntityId == null) { - agencyIdsByEntityId = new HashMap(); + agencyIdsByEntityId = new HashMap<>(); _agencyIdsByEntityClassAndId.put(entityType, agencyIdsByEntityId); } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/StopLocationFieldMappingImpl.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/StopLocationFieldMappingImpl.java index d53199b13..ca6a7a599 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/StopLocationFieldMappingImpl.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/serialization/mappings/StopLocationFieldMappingImpl.java @@ -45,7 +45,7 @@ public ConverterImpl(GtfsReaderContext context) { public Object convert(@SuppressWarnings("rawtypes") Class type, Object value) { if (type == String.class) { if (value instanceof String) - return (String) value; + return value; return null; } else if (type == StopLocation.class) { String entityId = value.toString(); @@ -57,13 +57,12 @@ public Object convert(@SuppressWarnings("rawtypes") Class type, Object value) { if (location != null) return location; Object locationGroup = _context.getEntity(LocationGroup.class, id); if (locationGroup != null) return locationGroup; - Object stopArea = _context.getEntity(StopArea.class, id); - if (stopArea != null) return stopArea; return null; } // we fell through -- unexpected situation - throw new ConversionException("Could not convert " + value + " of type " - + value.getClass() + " to " + type); + throw new ConversionException( + "Could not convert %s of type %s to %s".formatted( + value, value.getClass(), type)); } } } diff --git a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java index 9d51be156..b5045a68f 100644 --- a/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java +++ b/onebusaway-gtfs/src/main/java/org/onebusaway/gtfs/services/GtfsDao.java @@ -208,8 +208,6 @@ public interface GtfsDao extends GenericDao { public Collection getAllStopAreaElements(); - public Collection getAllStopAreas(); - public Collection getAllLocations(); public Collection getAllBookingRules(); diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java index 0745742de..04923bcc8 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FaresV2ReaderTest.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; @@ -34,12 +35,15 @@ import org.onebusaway.gtfs.model.FareTransferRule; import org.onebusaway.gtfs.model.RiderCategory; import org.onebusaway.gtfs.model.Route; +import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.model.StopAreaElement; import org.onebusaway.gtfs.services.GtfsRelationalDao; import org.onebusaway.gtfs.services.MockGtfs; public class FaresV2ReaderTest extends BaseGtfsTest { + private static final String AGENCY_ID = "1"; + @Test public void turlockFaresV2() throws CsvEntityIOException, IOException { String agencyId = "1642"; @@ -141,6 +145,30 @@ public void mdotMetroFaresV2() throws CsvEntityIOException, IOException { assertTrue(dao.hasFaresV2()); } + @Test + public void pierceTransitStopAreas() throws CsvEntityIOException, IOException { + var dao = processFeed(GtfsTestData.getPierceTransitFlex(), AGENCY_ID, false); + + var areaElements = List.copyOf(dao.getAllStopAreaElements()); + assertEquals(12, areaElements.size()); + + var first = areaElements.get(0); + assertEquals("1_4210813", first.getArea().getId().toString()); + var stop = first.getStop(); + assertEquals("4210806", stop.getId().getId()); + assertEquals("Bridgeport Way & San Francisco Ave SW (Northbound)", stop.getName()); + assertSame(Stop.class, stop.getClass()); + + var area = areaElements.get(0); + + assertSame(Stop.class, area.getStop().getClass()); + + var areas = List.copyOf(dao.getAllAreas()); + assertEquals(1, areas.size()); + + areas.forEach(stopArea -> assertFalse(stopArea.getStops().isEmpty())); + } + @Test public void testFaresV2Distance() throws IOException{ diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java index 7c98c328b..77cc8c455 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexDropOffSpellingTest.java @@ -41,10 +41,6 @@ public class FlexDropOffSpellingTest { @Test public void newSpelling() throws IOException { - testFlexStopTimeWithSpelling("drop_off"); - } - - private static void testFlexStopTimeWithSpelling(String dropOffSpelling) throws IOException { MockGtfs gtfs = MockGtfs.create(); gtfs.putMinimal(); gtfs.putDefaultTrips(); @@ -52,7 +48,7 @@ private static void testFlexStopTimeWithSpelling(String dropOffSpelling) throws String rows = String.format( "trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_booking_rule_id,drop_off_booking_rule_id,start_pickup_%s_window,end_pickup_%s_window", - dropOffSpelling, dropOffSpelling + "drop_off", "drop_off" ); gtfs.putLines( @@ -70,4 +66,5 @@ private static void testFlexStopTimeWithSpelling(String dropOffSpelling) throws assertEquals(LocalTime.parse("10:00").toSecondOfDay(), stopTime.getStartPickupDropOffWindow()); assertEquals(LocalTime.parse("18:00").toSecondOfDay(), stopTime.getEndPickupDropOffWindow()); } + } diff --git a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java index 0a523aa43..7d9826bb4 100644 --- a/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java +++ b/onebusaway-gtfs/src/test/java/org/onebusaway/gtfs/serialization/FlexReaderTest.java @@ -30,7 +30,6 @@ import org.onebusaway.gtfs.model.Location; import org.onebusaway.gtfs.model.LocationGroup; import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.StopArea; import org.onebusaway.gtfs.model.StopLocation; import org.onebusaway.gtfs.model.StopTime; @@ -38,53 +37,6 @@ public class FlexReaderTest extends BaseGtfsTest { private static final String AGENCY_ID = "1"; - @Test - public void pierceTransitStopAreas() throws CsvEntityIOException, IOException { - var dao = processFeed(GtfsTestData.getPierceTransitFlex(), AGENCY_ID, false); - - var areaElements = List.copyOf(dao.getAllStopAreaElements()); - assertEquals(15, areaElements.size()); - - var first = areaElements.get(0); - assertEquals("1_4210813", first.getArea().getId().toString()); - var stop = first.getStopLocation(); - assertEquals("4210806", stop.getId().getId()); - assertEquals("Bridgeport Way & San Francisco Ave SW (Northbound)", stop.getName()); - assertSame(Stop.class, stop.getClass()); - - var areaWithLocation = areaElements.stream().filter(a -> a.getId().toString().equals("1_4210800_area_1076")).findFirst().get(); - - var location = areaWithLocation.getStopLocation(); - assertSame(Location.class, location.getClass()); - - var stopAreas = List.copyOf(dao.getAllStopAreas()); - assertEquals(2, stopAreas.size()); - - var area = getArea(stopAreas, "1_4210813"); - assertEquals(12, area.getLocations().size()); - var stop2 = area.getLocations().stream().min(Comparator.comparing(StopLocation::getName)).get(); - assertEquals("Barnes Blvd & D St SW", stop2.getName()); - - var area2 = getArea(stopAreas, "1_4210800"); - assertEquals(3, area2.getLocations().size()); - - var names = area2.getLocations().stream().map(s -> s.getId().toString()).collect(Collectors.toSet()); - - assertEquals(Set.of("1_area_1075", "1_area_1074", "1_area_1076"), names); - - var trips = dao.getAllTrips(); - assertEquals(7, trips.size()); - - var trip = trips.stream().filter(t -> t.getId().getId().equals("t_5586096_b_80376_tn_0")).findFirst().get(); - var stopTimes = dao.getStopTimesForTrip(trip); - - var classes = stopTimes.stream().map(st -> st.getStop().getClass()).collect(Collectors.toList()); - assertEquals(List.of(StopArea.class, StopArea.class), classes); - - assertEquals("JBLM Stops", area.getName()); - - } - @Test public void locationIdAsASeparateColumn() throws CsvEntityIOException, IOException { var dao = processFeed(GtfsTestData.getBrownCountyFlex(), AGENCY_ID, false); @@ -125,7 +77,4 @@ public void locationGroupIdAsSeparateColumn() throws CsvEntityIOException, IOExc assertEquals(LocationGroup.class, second.getClass()); } - private static StopArea getArea(List stopAreas, String id) { - return stopAreas.stream().filter(a -> a.getId().toString().equals(id)).findAny().get(); - } } diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/areas.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/areas.txt index 6ee648fda..bd8614846 100644 --- a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/areas.txt +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/areas.txt @@ -1,3 +1,2 @@ area_id,area_name -4210800,Spanaway Core 4210813,JBLM Stops \ No newline at end of file diff --git a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/stop_areas.txt b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/stop_areas.txt index 6ed570846..06d91e6a4 100644 --- a/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/stop_areas.txt +++ b/onebusaway-gtfs/src/test/resources/org/onebusaway/gtfs/piercetransit-stop-areas-flex/stop_areas.txt @@ -1,7 +1,4 @@ area_id,stop_id -4210800,area_1074 -4210800,area_1075 -4210800,area_1076 4210813,4210804 4210813,4210805 4210813,4210806 From 62e13e4aa1a0212f887e8332f042a8ce0bf3a181 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 13:38:22 +0100 Subject: [PATCH 231/234] [maven-release-plugin] prepare release v5.0.0 --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 00693e922..4f7e5bb04 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 923646c06..ca8a7887a 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index e539ea62d..1633fadab 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 7cf559bab..014e6643e 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index b75d24501..7e63443f6 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.5.1-SNAPSHOT + 5.0.0 .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 660d5d3ca..5d6e57314 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 4.5.1-SNAPSHOT + 5.0.0 .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index db528f88a..a7281e0ef 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index ef48d944b..ddfc29092 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index 7f18e31a9..b0f44fab6 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 1023a8b02..17cd9628f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 4.5.1-SNAPSHOT + 5.0.0 pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - HEAD + v5.0.0 From c8f8c9afd4f62e4ead907e0d4a3d92a46e5859ee Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 13:38:24 +0100 Subject: [PATCH 232/234] [maven-release-plugin] prepare for next development iteration --- onebusaway-collections/pom.xml | 2 +- onebusaway-csv-entities/pom.xml | 2 +- onebusaway-gtfs-hibernate-cli/pom.xml | 2 +- onebusaway-gtfs-hibernate/pom.xml | 2 +- onebusaway-gtfs-merge-cli/pom.xml | 2 +- onebusaway-gtfs-merge/pom.xml | 2 +- onebusaway-gtfs-transformer-cli/pom.xml | 2 +- onebusaway-gtfs-transformer/pom.xml | 2 +- onebusaway-gtfs/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onebusaway-collections/pom.xml b/onebusaway-collections/pom.xml index 4f7e5bb04..cd2dbc9bc 100644 --- a/onebusaway-collections/pom.xml +++ b/onebusaway-collections/pom.xml @@ -10,7 +10,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index ca8a7887a..966aed5c1 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -4,7 +4,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-hibernate-cli/pom.xml b/onebusaway-gtfs-hibernate-cli/pom.xml index 1633fadab..a33852d67 100644 --- a/onebusaway-gtfs-hibernate-cli/pom.xml +++ b/onebusaway-gtfs-hibernate-cli/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT ../pom.xml onebusaway-gtfs-hibernate-cli diff --git a/onebusaway-gtfs-hibernate/pom.xml b/onebusaway-gtfs-hibernate/pom.xml index 014e6643e..fffd03d4f 100644 --- a/onebusaway-gtfs-hibernate/pom.xml +++ b/onebusaway-gtfs-hibernate/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT ../pom.xml diff --git a/onebusaway-gtfs-merge-cli/pom.xml b/onebusaway-gtfs-merge-cli/pom.xml index 7e63443f6..06c608f8b 100644 --- a/onebusaway-gtfs-merge-cli/pom.xml +++ b/onebusaway-gtfs-merge-cli/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 5.0.0 + 5.0.1-SNAPSHOT .. onebusaway-gtfs-merge-cli diff --git a/onebusaway-gtfs-merge/pom.xml b/onebusaway-gtfs-merge/pom.xml index 5d6e57314..d9d7c4ba2 100644 --- a/onebusaway-gtfs-merge/pom.xml +++ b/onebusaway-gtfs-merge/pom.xml @@ -3,7 +3,7 @@ onebusaway-gtfs-modules org.onebusaway - 5.0.0 + 5.0.1-SNAPSHOT .. onebusaway-gtfs-merge diff --git a/onebusaway-gtfs-transformer-cli/pom.xml b/onebusaway-gtfs-transformer-cli/pom.xml index a7281e0ef..df216e2b0 100644 --- a/onebusaway-gtfs-transformer-cli/pom.xml +++ b/onebusaway-gtfs-transformer-cli/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index ddfc29092..e9a034922 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT diff --git a/onebusaway-gtfs/pom.xml b/onebusaway-gtfs/pom.xml index b0f44fab6..838e04cce 100644 --- a/onebusaway-gtfs/pom.xml +++ b/onebusaway-gtfs/pom.xml @@ -9,7 +9,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 17cd9628f..26179aefa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.onebusaway onebusaway-gtfs-modules - 5.0.0 + 5.0.1-SNAPSHOT pom onebusaway-gtfs-modules @@ -38,7 +38,7 @@ scm:git:https://github.com/OneBusAway/onebusaway-gtfs-modules.git scm:git:ssh://git@github.com/OneBusAway/onebusaway-gtfs-modules.git https://github.com/OneBusAway/onebusaway-gtfs-modules - v5.0.0 + HEAD From 317b0cc565b12a5fc7aa05162bed5b6b39312a01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:39:15 +0000 Subject: [PATCH 233/234] Update dependency commons-beanutils:commons-beanutils to v1.10.0 --- onebusaway-csv-entities/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-csv-entities/pom.xml b/onebusaway-csv-entities/pom.xml index 966aed5c1..ee4732ba2 100644 --- a/onebusaway-csv-entities/pom.xml +++ b/onebusaway-csv-entities/pom.xml @@ -19,7 +19,7 @@ commons-beanutils commons-beanutils - 1.9.4 + 1.10.0 org.slf4j From f2bd4b0e3378fa793f5250b842dc58c77811572c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:39:19 +0000 Subject: [PATCH 234/234] Update dependency org.json:json to v20250107 --- onebusaway-gtfs-transformer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebusaway-gtfs-transformer/pom.xml b/onebusaway-gtfs-transformer/pom.xml index e9a034922..6035049e2 100644 --- a/onebusaway-gtfs-transformer/pom.xml +++ b/onebusaway-gtfs-transformer/pom.xml @@ -38,7 +38,7 @@ org.json json - 20241224 + 20250107 org.junit.jupiter