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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20