/[projects]/android/TrainInfoService/src/dk/thoerup/traininfoservice/banedk/DepartureFetcher.java
ViewVC logotype

Contents of /android/TrainInfoService/src/dk/thoerup/traininfoservice/banedk/DepartureFetcher.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1355 - (show annotations) (download)
Wed Apr 20 19:01:33 2011 UTC (13 years ago) by torben
File size: 17046 byte(s)
Move timeoutmap to GenericJavaUtils
1 package dk.thoerup.traininfoservice.banedk;
2
3
4 import java.net.URL;
5 import java.net.URLEncoder;
6 import java.util.Collections;
7 import java.util.Map;
8 import java.util.logging.Logger;
9
10 import org.jsoup.nodes.Document;
11 import org.jsoup.nodes.Element;
12 import org.jsoup.select.Elements;
13
14 import dk.thoerup.android.traininfo.common.DepartureBean;
15 import dk.thoerup.android.traininfo.common.DepartureEntry;
16 import dk.thoerup.android.traininfo.common.StationBean.StationEntry;
17 import dk.thoerup.circuitbreaker.CircuitBreaker;
18 import dk.thoerup.circuitbreaker.CircuitBreakerManager;
19 import dk.thoerup.genericjavautils.TimeoutMap;
20 import dk.thoerup.traininfoservice.Statistics;
21 import dk.thoerup.traininfoservice.TraininfoSettings;
22 import dk.thoerup.traininfoservice.db.StationDAO;
23
24 public class DepartureFetcher {
25
26 enum TrainType{
27 STOG,
28 REGIONAL
29 }
30
31 enum FetchTrainType {
32 STOG,
33 REGIONAL,
34 BOTH
35 }
36
37 Logger logger = Logger.getLogger(DepartureFetcher.class.getName());
38
39 Map<String, DepartureBean> cache;
40
41 StationDAO stationDao = new StationDAO();
42
43
44 private TraininfoSettings settings;
45
46 public DepartureFetcher(TraininfoSettings settings) {
47 this.settings = settings;
48 cache = new TimeoutMap<String,DepartureBean>( settings.getCacheTimeout() );
49 }
50
51
52
53
54 public DepartureBean cachedLookupDepartures(int stationID, boolean arrival, FetchTrainType type) throws Exception {
55
56 final String key = "" + stationID + ":" + arrival + ":" + type.toString();
57
58 DepartureBean departureBean = cache.get(key);
59
60
61 if (departureBean == null) {
62 departureBean = lookupDepartures(stationID, arrival, type);
63 cache.put(key, departureBean);
64 } else {
65 Statistics.getInstance().incrementDepartureCacheHits();
66 logger.info("Departure: Cache hit " + key); //remove before production
67 }
68 return departureBean;
69 }
70
71
72 public DepartureBean lookupDepartures(int stationID, boolean arrival, FetchTrainType type) throws Exception {
73
74 DepartureBean departureBean = new DepartureBean();
75
76 StationEntry station = stationDao.getById(stationID);
77
78 departureBean.stationName = station.getName();
79
80 if (station.getRegional() != null && (type == FetchTrainType.REGIONAL||type == FetchTrainType.BOTH) ) {
81 DepartureBean tempBean = lookupDepartures(station.getRegional(), TrainType.REGIONAL, arrival);
82 departureBean.entries.addAll( tempBean.entries );
83 departureBean.notifications.addAll(tempBean.notifications);
84 }
85
86 if (station.getStrain() != null && (type == FetchTrainType.STOG||type == FetchTrainType.BOTH)) {
87 DepartureBean tempBean = lookupDepartures(station.getStrain(), TrainType.STOG, arrival);
88 departureBean.entries.addAll( tempBean.entries );
89 departureBean.notifications.addAll(tempBean.notifications);
90 }
91
92 if (departureBean.entries.size() == 0) {
93 logger.info("No departures found for station " + stationID);
94 }
95
96 if (type == FetchTrainType.BOTH) { //if we have both S-tog and regional order by departure/arrival time
97 Collections.sort( departureBean.entries );
98 }
99
100
101 return departureBean;
102 }
103
104 public DepartureBean lookupDepartures(String stationcode, TrainType type, boolean arrival) throws Exception {
105 if ( settings.getUseAzureSite() == true) {
106 return lookupDeparturesAzureSite(stationcode, type, arrival);
107 } else {
108 return lookupDeparturesMobileSite(stationcode, type, arrival);
109 }
110 }
111
112 private String getTypeStringAzure(TrainType type) {
113 switch (type) {
114 case STOG:
115 return "S-Tog";
116 case REGIONAL:
117 return "Fjerntog";
118 default:
119 return ""; //Can not happen
120 }
121 }
122
123 private String getTypeStringWww(TrainType type) {
124 switch (type) {
125 case STOG:
126 return "S2";
127 case REGIONAL:
128 return "FJRN";
129 default:
130 return ""; //Can not happen
131 }
132 }
133
134 public DepartureBean lookupDeparturesAzureSite(String stationcode, TrainType type, boolean arrival) throws Exception {
135
136 DepartureBean departureBean = new DepartureBean();
137
138
139 String typeString = getTypeStringAzure(type);
140 String arrivalDeparture = (arrival==false) ? "Afgang" : "Ankomst";
141
142 stationcode = URLEncoder.encode(stationcode,"ISO-8859-1");
143
144 String uri = "http://trafikinfo.bane.dk/Trafikinformation/AfgangAnkomst/" + arrivalDeparture + "/" + stationcode + "/" + typeString + "/UdvidetVisning";
145
146 logger.fine("URI: " + uri);
147 JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
148 CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
149
150 Document page = (Document) breaker.invoke(wrapper);
151
152 String tableName = arrival == false ? "afgangtabel" : "ankomsttabel";
153 Element table = page.getElementById(tableName);
154
155 if (table != null) {
156 Elements tableRows = table.getElementsByTag("tr");
157
158 //boolean tidsstregExists = (table.getElementsByAttributeValue("class", "Tidsstreg").size() > 0);
159 //boolean passedTidsstreg = false;
160
161 for (Element currentRow : tableRows) {
162 String rowClass = currentRow.attr("class");
163 /*
164 if (tidsstregExists == true && passedTidsstreg == false) {
165 if (currentRow.getElementsByAttributeValue("class", "Tidsstreg").size() > 0) {
166 passedTidsstreg = true;
167 } else {
168 continue;
169 }
170 }*/
171
172 if (rowClass != null && rowClass.toLowerCase().contains("station") ) {
173
174 Elements fields = currentRow.getElementsByTag("td");
175
176 DepartureEntry departure = new DepartureEntry();
177
178 String time = fields.get(0).text();
179 if (time.equals(""))
180 time = "0:00"; //Bane.dk bug work-around
181 departure.setTime(time);
182
183 int updated = extractUpdated( fields.get(1) );
184 departure.setUpdated(updated);
185
186 String trainNumber = fields.get(2).text();
187 if (type == TrainType.STOG) //If it is S-train we need to extract the trainNumber
188 trainNumber = trainNumber + " " + extractTrainNumberAzure(fields.get(2));
189 departure.setTrainNumber(trainNumber);
190
191 String destination = fields.get(3).text();
192 departure.setDestination(destination);
193
194 String origin = fields.get(4).text();
195 departure.setOrigin(origin);
196
197 String location = fields.get(5).text();
198 departure.setLocation(location);
199
200 String status = fields.get(6).text().trim();
201 departure.setStatus(status);
202
203 String note = extractNote( fields.get(7) );
204 departure.setNote(note);
205
206 departure.setType(typeString);
207
208 departureBean.entries.add( departure );
209 }
210 }
211 } else {
212 logger.warning("No departures found for station=" + stationcode + ", type=" + type);
213 }
214
215 Element notifDiv = page.getElementById("station_planlagte_text");
216 if (notifDiv != null) {
217
218 Elements tables = notifDiv.getElementsByTag("table");
219 for (Element tab : tables) {
220
221 Elements anchors = tab.getElementsByTag("a");
222 if (anchors.size() == 2) {
223 departureBean.notifications.add( anchors.get(1).text() );
224 }
225 }
226
227 }
228
229
230 return departureBean;
231 }
232
233 public DepartureBean lookupDeparturesMobileSite(String stationcode, TrainType traintype, boolean arrival) throws Exception {
234
235 DepartureBean departureBean = new DepartureBean();
236
237
238 String typeString = getTypeStringWww(traintype);
239 String arrivalDeparture = (arrival==false) ? "afgang" : "ankomst";
240
241 stationcode = URLEncoder.encode(stationcode,"ISO-8859-1");
242
243 //String uri = "http://trafikinfo.bane.dk/Trafikinformation/AfgangAnkomst/" + arrivalDeparture + "/" + stationcode + "/" + typeString + "/UdvidetVisning";
244 String uri = "http://mobil.bane.dk/mobilStation.asp?artikelID=5332&stat_kode=" + stationcode + "&webprofil=" + typeString +"&beskrivelse=&mode=ankomstafgang&ankomstafgang=" + arrivalDeparture + "&gemstation=&fuldvisning=1";
245 logger.fine("URI: " + uri);
246 JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
247 CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
248
249 Document page = (Document) breaker.invoke(wrapper);
250
251
252 Element content = page.getElementsByClass("contentDiv").get(0);
253
254
255 if (content != null) {
256 Elements tableRows = content.child(0).children();
257
258
259
260 for (Element currentRow : tableRows) {
261 if (currentRow.tagName().equals("br") ) {
262 break;
263 }
264
265
266 String link = currentRow.child(0).attr("href");
267
268 logger.fine( currentRow.text() );
269 logger.fine("Href: " + link);
270
271
272 String parts[] = currentRow.text().split(",");
273
274
275 DepartureEntry departure = new DepartureEntry();
276
277 //if we do these things upfront, then we are allowed to use continue statement when row contains no more data
278 departure.setType(typeString);
279 departureBean.entries.add( departure );
280
281 /*
282 http://mobil.bane.dk/mobilStation.asp?artikelID=5332&tognummer=111&webprofil=FJRN&mode=rute&strBemaerkning=Afg%E5r+fra+%C5rhus+H+kl%2E07%3A21++&strRefURL=%2FmobilStation%2Easp%3FartikelID%3D5332%26stat%5Fkode%3DAR%26webprofil%3DFJRN%26beskrivelse%3D%25C5rhus%2BH%26mode%3Dankomstafgang%26ankomstafgang%3Dafgang%26gemstation%3D
283 */
284 int offset = 0;
285
286 String time = parts[offset++];
287 if (time.equals(""))
288 time = "0:00"; //Bane.dk bug work-around
289 departure.setTime(time);
290
291 int updated = 4; //does not exist on mobile
292 departure.setUpdated(updated);
293
294 String trainNumber = "-"; //extractTrainNumberAzure(fields.get(2));
295 /*if (traintype == TrainType.STOG) //If it is S-train we need to extract the trainNumber
296 trainNumber = trainNumber + " " + extractTrainNumberAzure(fields.get(2));*/
297 departure.setTrainNumber(trainNumber);
298
299 if (traintype == TrainType.STOG) { //if it is stog the next vield is the "Line" code - this should be used somewhere, but skippint ahead for now
300 offset++;
301 }
302
303 String destination = parts[offset++];
304 departure.setDestination(destination);
305
306 String origin = "-"; // fields.get(4).text(); does not exist on mobile
307 departure.setOrigin(origin);
308
309 String location = ""; // fields.get(5).text(); does not exist on mobile
310 departure.setLocation(location);
311
312 if (offset == parts.length) {
313 continue;
314 }
315
316 if (parts[offset].trim().equalsIgnoreCase("NB!")) {
317 offset++;
318 }
319
320 if (offset == parts.length) {
321 continue;
322 }
323
324 String status = parts[offset++]; //fields.get(6).text().trim(); - extract from url
325 departure.setStatus(status);
326
327 String note = ""; //extractNote( fields.get(7) ); - extract from url
328 departure.setNote(note);
329
330 }
331 } else {
332 logger.warning("No departures found for station=" + stationcode + ", type=" + traintype);
333 }
334
335 return departureBean;
336 }
337
338
339
340 public static String cleanText(String input) {
341 //apparently JSoup translates &nbsp; characters on www.bane.dk to 0xA0
342 return input.replace((char) 0xA0, (char)0x20).trim();
343 }
344
345
346 // old www site is not available any more
347 @Deprecated
348 public DepartureBean lookupDeparturesWwwSite(String stationcode, TrainType trainType, boolean arrival) throws Exception {
349
350 DepartureBean departureBean = new DepartureBean();
351
352 String type = getTypeStringWww(trainType);
353
354 stationcode = URLEncoder.encode(stationcode, "ISO-8859-1");
355
356
357 String uri = "http://www.bane.dk/visStation.asp?ArtikelID=4275&W=" + type + "&S=" + stationcode;
358 logger.fine("URI:" + uri);
359
360
361 JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
362 CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
363
364 Element page = (Element) breaker.invoke(wrapper);
365
366 String tableName = arrival == false ? "afgangtabel" : "ankomsttabel";
367 Element table = page.getElementById(tableName);
368
369
370
371 if (table != null) {
372 Elements tableRows = table.getElementsByTag("tr");
373
374 //boolean passedTidsstreg = false;
375 //boolean tidsstregExists = (table.getElementsByAttributeValue("class", "Tidsstreg").size() > 0);
376
377 for (Element currentRow : tableRows) {
378 String rowClass = currentRow.attr("class");
379 /*
380 if (tidsstregExists == true && passedTidsstreg == false) {
381 if (currentRow.getElementsByAttributeValue("class", "Tidsstreg").size() > 0) {
382 passedTidsstreg = true;
383 } else {
384 continue;
385 }
386 }*/
387
388
389 if (rowClass != null && rowClass.toLowerCase().contains("station") ) {
390 Elements fields = currentRow.getElementsByTag("td");
391
392 DepartureEntry departure = new DepartureEntry();
393
394
395
396 String time = cleanText( fields.get(0).getAllElements().get(2).text() );
397 if (time.equals(""))
398 time = "0:00"; //Bane.dk bug work-around
399 departure.setTime(time);
400
401 int updated = extractUpdated( fields.get(1) );
402 departure.setUpdated(updated);
403
404 String trainNumber = cleanText( fields.get(2).text() );
405 if (type.equalsIgnoreCase("S2")) //If it is S-train we need to extract the trainNumber
406 trainNumber = trainNumber + " " + extractTrainNumberWww(fields.get(2));
407 departure.setTrainNumber(trainNumber);
408
409 String destination = cleanText( fields.get(3).text() );
410 departure.setDestination(destination);
411
412 String origin = cleanText( fields.get(4).text() );
413 departure.setOrigin(origin);
414
415 String location = cleanText( fields.get(5).text() );
416 departure.setLocation(location);
417
418 String status = cleanText( fields.get(6).text() );
419 departure.setStatus(status);
420
421 String note = cleanText( extractNote( fields.get(7) ) );
422 departure.setNote(note);
423
424 departure.setType(type);
425
426 departureBean.entries.add(departure);
427
428
429 }
430 }
431 } else {
432 logger.warning("No departures found for station=" + stationcode + ", type=" + type);
433 }
434
435
436 return departureBean;
437 }
438
439
440 private int extractUpdated(Element updatedTd) { //extract the digit (in this case: 4) from "media/trafikinfo/opdater4.gif"
441 int updated = -1;
442
443 Elements updatedImgs = updatedTd.getElementsByTag("img");
444 String updatedStr = updatedImgs.get(0).attr("src");
445
446 if (updatedStr != null) {
447 for (int i=0; i<updatedStr.length(); i++) {
448 char c = updatedStr.charAt(i);
449 if ( Character.isDigit(c)) {
450 updated = Character.digit(c, 10);
451 break;
452 }
453 }
454 }
455 return updated;
456 }
457
458 private String extractNote(Element noteTd) {
459 String note = noteTd.text().trim();
460
461
462 Elements elems = noteTd.getElementsByClass("bemtype");
463 if (elems.size() > 0 && note.charAt(note.length()-1) == 'i')
464 note = note.substring(0,note.length() -1 );
465
466 return note.trim();
467 }
468
469 private String extractTrainNumberAzure(Element trainTd) {
470 Element anchorElement = trainTd.getElementsByTag("a").get(0);
471 String href = anchorElement.attr("href");
472
473 int pos = href.lastIndexOf('/');
474 String number = href.substring(pos+1);
475
476 return number;
477 }
478
479 private String extractTrainNumberWww(Element trainTd) {
480 String number = "";
481 Element anchorElement = trainTd.getElementsByTag("a").get(0);
482 String href = anchorElement.attr("href");
483 String argstring = href.substring( href.indexOf('?') + 1);
484
485 String args[] = argstring.split("&");
486 for (String arg : args) {
487 String pair[] = arg.split("="); // Key=pair[0], Value=pair[1]
488
489 if (pair[0].equalsIgnoreCase("TogNr"))
490 number = pair[1];
491 }
492
493
494 return number;
495 }
496
497
498 //test
499 /*
500 public static void main(String args[]) throws Exception {
501 DepartureFetcher f = new DepartureFetcher();
502 List<DepartureBean> deps = f.lookupDepartures("AR", "FJRN");
503 for(DepartureBean d : deps) {
504 System.out.println( d.getTime() + ";" + d.getUpdated() + ";" + d.getTrainNumber() + ";" +
505 d.getDestination() + ";" + d.getOrigin() + ";" + d.getLocation() + ";" + d.getStatus() + ";" + d.getNote() );
506 }
507
508 System.out.println("--------------------------");
509 }*/
510 }

  ViewVC Help
Powered by ViewVC 1.1.20