Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report issue for duplicate journey patterns in NeTEx #5571

Conversation

habrahamsson-skanetrafiken
Copy link
Contributor

Summary

As a follow-up to #5552, this PR reports an issue to the issueStore if there are duplicate journeypatterns in the Netex data.

  • A small refactor to make TripPatternMapper less stateful and also make it easier to get the tripPatternId.
  • Report DuplicateJourneyPattern issue for duplicate journey patterns.

Unit tests

Had to do some minor touches to the TripPatternMapperTest.

Bumping the serialization version id

No

@habrahamsson-skanetrafiken habrahamsson-skanetrafiken added Improvement NeTEx This issue is related to the Netex model/import. Skip Changelog labels Dec 13, 2023
@habrahamsson-skanetrafiken habrahamsson-skanetrafiken requested a review from a team as a code owner December 13, 2023 13:54
Copy link

codecov bot commented Dec 13, 2023

Codecov Report

Attention: 34 lines in your changes are missing coverage. Please review.

Comparison is base (d9b59c1) 67.21% compared to head (2c86a46) 67.34%.
Report is 182 commits behind head on dev-2.x.

Files Patch % Lines
...mework/application/ApplicationShutdownSupport.java 0.00% 9 Missing ⚠️
...er/ext/siri/updater/SiriETGooglePubsubUpdater.java 0.00% 6 Missing ⚠️
...t/siri/updater/azure/AbstractAzureSiriUpdater.java 0.00% 6 Missing ⚠️
...entripplanner/standalone/server/GrizzlyServer.java 0.00% 4 Missing ⚠️
...org/opentripplanner/netex/mapping/NetexMapper.java 85.71% 2 Missing and 1 partial ⚠️
...entripplanner/netex/mapping/TripPatternMapper.java 90.90% 2 Missing ⚠️
...n/java/org/opentripplanner/standalone/OTPMain.java 0.00% 2 Missing ⚠️
...org/opentripplanner/standalone/OtpStartupInfo.java 0.00% 2 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             dev-2.x    #5571      +/-   ##
=============================================
+ Coverage      67.21%   67.34%   +0.13%     
- Complexity     16086    16149      +63     
=============================================
  Files           1852     1857       +5     
  Lines          71003    71082      +79     
  Branches        7409     7404       -5     
=============================================
+ Hits           47723    47872     +149     
+ Misses         20813    20749      -64     
+ Partials        2467     2461       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 464 to 466
tripPatternMapper
.mapTripPattern(journeyPattern)
.ifPresent(result -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling a none-trivial code block inside a function chain is not clean. Consider making a private method for the code block or using Java language features like if/for.

TripPatternMapperResult mapTripPattern(JourneyPattern_VersionStructure journeyPattern) {
// Make sure the result is clean, by creating a new object.
result = new TripPatternMapperResult();
Optional<TripPatternMapperResult> mapTripPattern(JourneyPattern_VersionStructure journeyPattern) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observation: The original result class did handle the empty case, so this is weakening the existing functionality. The good thing is that a it is a bit less code to maintain, but since this does not reduce the code in the caller - it complicates it, than gain is minimal. I do not suggest to change it, but just wanted to mention it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make it explicit that if we return a result then it will have a single TripPattern. I could have added an optional/nullable tripPattern field to the result class instead but that wouldn't really be simpler and i think this better expresses the actual behaviour of the mapTripPattern method.

Now the caller has to handle the case where the result is empty, it's a bit more complicated at the call site but IMO it's worth it.

Copy link
Member

@t2gran t2gran Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that you are wrapping a result wrapper with another result wrapper - Optional and TripPatternMapperResult have some of the same responsibilities. You should not wrap the field inside TripPatternMapperResult with optional. We are return a collection here, not a single instance - you could provide a isEmpty() or ifPresent() method to make it explicit, but it was not needed; hence not added. The pattern we follow in OTP for collections is to return empty collections, not null or Optional.

Copy link
Member

@t2gran t2gran Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing is that we avoid using records with arrays and collections. Arrays can not be protected, while collections are hard to protect - classes are simpler/less error prune. Even if the TripPatternMapperResult is just a DTO here, people have a tendency to copy code/and ways of doing things - so I want it to be clean everywhere - even if it in this case is ok/no risk.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that you are wrapping a result wrapper with another result wrapper - Optional and TripPatternMapperResult have some of the same responsibilities.

In this PR my idea is to change the responsibilities so that the result class is only responsible for representing an actual result. The Optional is responsible for representing presence/absence of a result. I think this is a pretty common pattern. And I think it is a good pattern since the result class would only have to concern itself with a single responsibility.

We are return a collection here, not a single instance - you could provide a isEmpty() or ifPresent() method to make it explicit, but it was not needed; hence not added. The pattern we follow in OTP for collections is to return empty collections, not null or Optional.

The thing is that I want the result to have a single instance of a TripPattern instead of a collection of TripPatterns. Because I want the caller to be able to see what the resulting id is and I wanted it to be obvous that the issue for duplicated ids is only reported once.

In this codebase, would it be more idiomatic to put a List<TripPattern> in the result and have it be implicit that this is either empty or has a single entry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing is that we avoid using records with arrays and collections. Arrays can not be protected, while collections are hard to protect - classes are simpler/less error prune. Even if the TripPatternMapperResult is just a DTO here, people have a tendency to copy code/and ways of doing things - so I want it to be clean everywhere - even if it in this case is ok/no risk.

Is this in order to protect the collections from mutation? I think that's a good concern. But then having collections as public attributes should be just as bad, or am I missing something? In this refactored code it wouldn't be that hard to make the collections unmodifiable when creating the record to protect them from modification. It's not perfect but I think it's a win for this style.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this codebase, would it be more idiomatic to put a List in the result and have it be implicit that this is either empty or has a single entry?

Of cause not. The TripPatternMapperResult has a single-responsibility - to carry the result of one method. The result has many different states, which you toke just one out.

But we can agree to disagree - that is fine. I will approve the PR.

@habrahamsson-skanetrafiken habrahamsson-skanetrafiken merged commit aaaedbe into opentripplanner:dev-2.x Jan 9, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Improvement NeTEx This issue is related to the Netex model/import. Skip Changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants