12
12
import javax .annotation .Nonnull ;
13
13
import org .opentripplanner .transit .model .network .StopPattern ;
14
14
import org .opentripplanner .transit .model .network .TripPattern ;
15
- import org .opentripplanner .transit .model .network .TripPatternBuilder ;
16
15
import org .opentripplanner .transit .model .site .RegularStop ;
17
16
import org .opentripplanner .transit .model .site .StopLocation ;
18
17
import org .opentripplanner .transit .model .timetable .Trip ;
19
18
import org .slf4j .Logger ;
20
19
import org .slf4j .LoggerFactory ;
21
20
22
21
/**
23
- * A synchronized cache of trip patterns that are added to the graph due to GTFS-realtime messages.
22
+ * Threadsafe mechanism for tracking any TripPatterns added to the graph via SIRI realtime messages.
23
+ * This tracks only patterns added by realtime messages, not ones that already existed from the
24
+ * scheduled NeTEx. This is a "cache" in the sense that it will keep returning the same TripPattern
25
+ * when presented with the same StopPattern, so if realtime messages add many trips passing through
26
+ * the same sequence of stops, they will all end up on this same TripPattern.
27
+ * <p>
28
+ * Note that there are two versions of this class, this one for GTFS-RT and another for SIRI.
29
+ * See additional comments in the Javadoc of the GTFS-RT version of this class, whose name is
30
+ * simply TripPatternCache.
31
+ * TODO RT_AB: To the extent that double SIRI/GTFS implementations are kept, prefix all names
32
+ * with GTFS or SIRI or NETEX rather than having no prefix on the GTFS versions.
33
+ * TODO RT_TG: There is no clear strategy for what should be in the cache and the transit model and the flow
34
+ * between them. The NeTEx and a GTFS version of this should be merged. Having NeTex and GTFS
35
+ * specific indexes inside is ok. With the increased usage of DatedServiceJourneys, this should probably
36
+ * be part of the main model - not a separate cashe. It is possible that this class works when it comes to
37
+ * the thread-safety, but just by looking at a few lines of code I see problems - a strategy needs to be
38
+ * analysed, designed and documented.
24
39
*/
25
40
public class SiriTripPatternCache {
26
41
27
- private static final Logger log = LoggerFactory .getLogger (SiriTripPatternCache .class );
42
+ private static final Logger LOG = LoggerFactory .getLogger (SiriTripPatternCache .class );
28
43
44
+ // TODO RT_AB: Improve documentation. This seems to be the primary collection of added
45
+ // TripPatterns, with other collections serving as indexes. Similar to TripPatternCache.cache
46
+ // in the GTFS version of this class, but with service date as part of the key.
29
47
private final Map <StopPatternServiceDateKey , TripPattern > cache = new HashMap <>();
30
48
49
+ // TODO RT_AB: Improve documentation. This field appears to be an index that exists only in the
50
+ // SIRI version of this class (i.e. this version and not the older TripPatternCache that
51
+ // handles GTFS-RT). This index appears to be tailored for use by the Transmodel GraphQL APIs.
31
52
private final ListMultimap <StopLocation , TripPattern > patternsForStop = Multimaps .synchronizedListMultimap (
32
53
ArrayListMultimap .create ()
33
54
);
34
55
56
+ // TODO RT_AB: clarify name and add documentation to this field.
35
57
private final Map <TripServiceDateKey , TripPattern > updatedTripPatternsForTripCache = new HashMap <>();
36
58
59
+ // TODO RT_AB: generalize this so we can generate IDs for SIRI or GTFS-RT sources.
37
60
private final SiriTripPatternIdGenerator tripPatternIdGenerator ;
61
+
38
62
private final Function <Trip , TripPattern > getPatternForTrip ;
39
63
64
+ /**
65
+ * TODO RT_AB: This class could potentially be reused for both SIRI and GTFS-RT, which may
66
+ * involve injecting a different ID generator and pattern fetching method.
67
+ *
68
+ * @param getPatternForTrip SiriTripPatternCache needs only this one feature of TransitService, so we retain
69
+ * only this function reference to effectively narrow the interface. This should also facilitate
70
+ * testing.
71
+ */
40
72
public SiriTripPatternCache (
41
73
SiriTripPatternIdGenerator tripPatternIdGenerator ,
42
74
Function <Trip , TripPattern > getPatternForTrip
@@ -46,8 +78,13 @@ public SiriTripPatternCache(
46
78
}
47
79
48
80
/**
49
- * Get cached trip pattern or create one if it doesn't exist yet. If a trip pattern is created,
50
- * vertices and edges for this trip pattern are also created in the transitModel.
81
+ * Get cached trip pattern or create one if it doesn't exist yet.
82
+ *
83
+ * TODO RT_AB: Improve documentation and/or merge with GTFS version of this class.
84
+ * This was clearly derived from a method from TripPatternCache. This is the only non-dead-code
85
+ * public method on this class, and mirrors the only public method on the GTFS-RT version of
86
+ * TripPatternCache. It also explains why this class is called a "cache". It allows reusing the
87
+ * same TripPattern instance when many different trips are created or updated with the same pattern.
51
88
*
52
89
* @param stopPattern stop pattern to retrieve/create trip pattern
53
90
* @param trip Trip containing route of new trip pattern in case a new trip pattern will be
@@ -61,6 +98,9 @@ public synchronized TripPattern getOrCreateTripPattern(
61
98
) {
62
99
TripPattern originalTripPattern = getPatternForTrip .apply (trip );
63
100
101
+ // TODO RT_AB: Verify implementation, which is different than the GTFS-RT version.
102
+ // It can return a TripPattern from the scheduled data, but protective copies are handled in
103
+ // TimetableSnapshot.update. Better document this aspect of the contract in this method's Javadoc.
64
104
if (originalTripPattern .getStopPattern ().equals (stopPattern )) {
65
105
return originalTripPattern ;
66
106
}
@@ -72,56 +112,57 @@ public synchronized TripPattern getOrCreateTripPattern(
72
112
// Create TripPattern if it doesn't exist yet
73
113
if (tripPattern == null ) {
74
114
var id = tripPatternIdGenerator .generateUniqueTripPatternId (trip );
75
- TripPatternBuilder tripPatternBuilder = TripPattern
76
- .of (id )
77
- .withRoute (trip .getRoute ())
78
- .withMode (trip .getMode ())
79
- .withNetexSubmode (trip .getNetexSubMode ())
80
- .withStopPattern (stopPattern );
81
-
82
- // TODO - SIRI: Add pattern to transitModel index?
83
-
84
- tripPatternBuilder .withCreatedByRealtimeUpdater (true );
85
- tripPatternBuilder .withOriginalTripPattern (originalTripPattern );
86
-
87
- tripPattern = tripPatternBuilder .build ();
115
+ tripPattern =
116
+ TripPattern
117
+ .of (id )
118
+ .withRoute (trip .getRoute ())
119
+ .withMode (trip .getMode ())
120
+ .withNetexSubmode (trip .getNetexSubMode ())
121
+ .withStopPattern (stopPattern )
122
+ .withCreatedByRealtimeUpdater (true )
123
+ .withOriginalTripPattern (originalTripPattern )
124
+ .build ();
125
+ // TODO: Add pattern to transitModel index?
88
126
89
127
// Add pattern to cache
90
128
cache .put (key , tripPattern );
91
129
}
92
130
93
- /**
94
- *
95
- * When the StopPattern is first modified (e.g. change of platform), then updated (or vice versa), the stopPattern is altered, and
96
- * the StopPattern-object for the different states will not be equal.
97
- *
98
- * This causes both tripPatterns to be added to all unchanged stops along the route, which again causes duplicate results
99
- * in departureRow-searches (one departure for "updated", one for "modified").
100
- *
101
- * Full example:
102
- * Planned stops: Stop 1 - Platform 1, Stop 2 - Platform 1
103
- *
104
- * StopPattern #rt1: "updated" stopPattern cached in 'patternsForStop':
105
- * - Stop 1, Platform 1
106
- * - StopPattern #rt1
107
- * - Stop 2, Platform 1
108
- * - StopPattern #rt1
109
- *
110
- * "modified" stopPattern: Stop 1 - Platform 1, Stop 2 - Platform 2
111
- *
112
- * StopPattern #rt2: "modified" stopPattern cached in 'patternsForStop' will then be:
113
- * - Stop 1, Platform 1
114
- * - StopPattern #rt1, StopPattern #rt2
115
- * - Stop 2, Platform 1
116
- * - StopPattern #rt1
117
- * - Stop 2, Platform 2
118
- * - StopPattern #rt2
119
- *
120
- *
121
- * Therefore, we must cleanup the duplicates by deleting the previously added (and thus outdated)
122
- * tripPattern for all affected stops. In example above, "StopPattern #rt1" should be removed from all stops
123
- *
124
- */
131
+ /*
132
+ When the StopPattern is first modified (e.g. change of platform), then updated (or vice
133
+ versa), the stopPattern is altered, and the StopPattern-object for the different states will
134
+ not be equal.
135
+
136
+ This causes both tripPatterns to be added to all unchanged stops along the route, which again
137
+ causes duplicate results in departureRow-searches (one departure for "updated", one for
138
+ "modified").
139
+
140
+ Full example:
141
+ Planned stops: Stop 1 - Platform 1, Stop 2 - Platform 1
142
+
143
+ StopPattern #rt1: "updated" stopPattern cached in 'patternsForStop':
144
+ - Stop 1, Platform 1
145
+ - StopPattern #rt1
146
+ - Stop 2, Platform 1
147
+ - StopPattern #rt1
148
+
149
+ "modified" stopPattern: Stop 1 - Platform 1, Stop 2 - Platform 2
150
+
151
+ StopPattern #rt2: "modified" stopPattern cached in 'patternsForStop' will then be:
152
+ - Stop 1, Platform 1
153
+ - StopPattern #rt1, StopPattern #rt2
154
+ - Stop 2, Platform 1
155
+ - StopPattern #rt1
156
+ - Stop 2, Platform 2
157
+ - StopPattern #rt2
158
+
159
+ Therefore, we must clean up the duplicates by deleting the previously added (and thus
160
+ outdated) tripPattern for all affected stops. In example above, "StopPattern #rt1" should be
161
+ removed from all stops.
162
+
163
+ TODO RT_AB: review why this particular case is handled in an ad-hoc manner. It seems like all
164
+ such indexes should be constantly rebuilt and versioned along with the TimetableSnapshot.
165
+ */
125
166
TripServiceDateKey tripServiceDateKey = new TripServiceDateKey (trip , serviceDate );
126
167
if (updatedTripPatternsForTripCache .containsKey (tripServiceDateKey )) {
127
168
// Remove previously added TripPatterns for the trip currently being updated - if the stopPattern does not match
@@ -132,16 +173,14 @@ public synchronized TripPattern getOrCreateTripPattern(
132
173
patternsForStop .values ().removeAll (Arrays .asList (cachedTripPattern ));
133
174
int sizeAfter = patternsForStop .values ().size ();
134
175
135
- log .debug (
176
+ LOG .debug (
136
177
"Removed outdated TripPattern for {} stops in {} ms - tripId: {}" ,
137
178
(sizeBefore - sizeAfter ),
138
179
(System .currentTimeMillis () - t1 ),
139
180
trip .getId ()
140
181
);
141
- /*
142
- TODO: Also remove previously updated - now outdated - TripPattern from cache ?
143
- cache.remove(new StopPatternServiceDateKey(cachedTripPattern.stopPattern, serviceDate));
144
- */
182
+ // TODO: Also remove previously updated - now outdated - TripPattern from cache ?
183
+ // cache.remove(new StopPatternServiceDateKey(cachedTripPattern.stopPattern, serviceDate));
145
184
}
146
185
}
147
186
@@ -160,6 +199,7 @@ public synchronized TripPattern getOrCreateTripPattern(
160
199
161
200
/**
162
201
* Returns any new TripPatterns added by real time information for a given stop.
202
+ * TODO RT_AB: this appears to be currently unused. Perhaps remove it if the API has changed.
163
203
*
164
204
* @param stop the stop
165
205
* @return list of TripPatterns created by real time sources for the stop.
@@ -169,6 +209,16 @@ public List<TripPattern> getAddedTripPatternsForStop(RegularStop stop) {
169
209
}
170
210
}
171
211
212
+ // TODO RT_AB: move the below classes inside the above class as private static inner classes.
213
+ // Defining these additional classes in the same top-level class file is unconventional.
214
+
215
+ /**
216
+ * Serves as the key for the collection of TripPatterns added by realtime messages.
217
+ * Must define hashcode and equals to confer semantic identity.
218
+ * TODO RT_AB: clarify why each date has a different TripPattern instead of a different Timetable.
219
+ * It seems like there's a separate TripPattern instance for each StopPattern and service date,
220
+ * rather a single TripPattern instance associated with a separate timetable for each date.
221
+ */
172
222
class StopPatternServiceDateKey {
173
223
174
224
StopPattern stopPattern ;
@@ -194,6 +244,11 @@ public boolean equals(Object thatObject) {
194
244
}
195
245
}
196
246
247
+ /**
248
+ * An alternative key for looking up realtime-added TripPatterns by trip and service date instead
249
+ * of stop pattern and service date. Must define hashcode and equals to confer semantic identity.
250
+ * TODO RT_AB: verify whether one map is considered the definitive collection and the other an index.
251
+ */
197
252
class TripServiceDateKey {
198
253
199
254
Trip trip ;
0 commit comments