diff --git a/src/de/schildbach/pte/OebbProvider.java b/src/de/schildbach/pte/OebbProvider.java index 21c148cf..b34bcdfa 100644 --- a/src/de/schildbach/pte/OebbProvider.java +++ b/src/de/schildbach/pte/OebbProvider.java @@ -24,7 +24,7 @@ public class OebbProvider implements NetworkProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES || capability == Capability.LOCATION_STATION_ID) + if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID) return true; return false; @@ -157,7 +157,7 @@ public class OebbProvider implements NetworkProvider private static final String QUERY_CONNECTIONS_FORM_URL = "http://fahrplan.oebb.at/bin/query.exe/dn?"; private static final Pattern P_QUERY_CONNECTIONS_FORM_ACTION = Pattern - .compile("
]*>.*?" // action - + "\n(.*?)\n
.*?" // header - + "\n" // - + "(.*?
(.*?)
.*?)\n" // connections overview - + ".*?" // - + "\n" // - + "(.*?)\n" // connection details - + "
\n"); private static final Pattern P_CONNECTIONS_HEAD = Pattern.compile(".*?" // - + "von:.*?]*>\\s*(.*?)\\s*.*?" // from - + "Datum:.*?]*>.., (\\d{2}\\.\\d{2}\\.\\d{2}).*?" // date - + "nach:.*?]*>\\s*(.*?)\\s*.*?" // to - + "(?:\"(REQ0HafasScrollDir=2&guiVCtrl_connection_detailsOut_add_selection&)\".*?)?" // linkEarlier - + "(?:\"(REQ0HafasScrollDir=1&guiVCtrl_connection_detailsOut_add_selection&)\".*?)?" // linkLater + + "von:\n\\s*(.*?)\\s*.*?" // from + + "nach:\n\\s*(.*?)\\s*.*?" // to + + "\nDatum:\n\n.., (\\d{2}\\.\\d{2}\\.\\d{2}).*?" // date + + "(?:]*>\n(.*?)\n" // + + "\n(.*?)Seitenanfang.*?" // , Pattern.DOTALL); - private static final Pattern P_CONNECTIONS_COARSE = Pattern.compile("\n(.*?)", Pattern.DOTALL); private static final Pattern P_CONNECTIONS_FINE = Pattern.compile(".*?" // - + "name=\"guiVCtrl_connection_detailsOut_select_([\\w-]+)\".*?" // id - + "]*>(\\d{2}\\.\\d{2}\\.\\d{2})" // departureDate + + "]*>(\\d{2}\\.\\d{2}\\.\\d{2})" // departureDate + "(?:
(\\d{2}\\.\\d{2}\\.\\d{2}))?.*?" // arrivalDate - + "(\\d{1,2}:\\d{2})" // departureTime - + "
(\\d{1,2}:\\d{2}).*?" // arrivalTime + + "(\\d{1,2}:\\d{2}) ab.*?" // departureTime + + "(\\d{1,2}:\\d{2}) an.*?" // arrivalTime + , Pattern.DOTALL); + private static final Pattern P_CONNECTION_DETAILS_COARSE = Pattern.compile("" // + + "\n(.*?)\n" // + + "\n(.*?)\n" // + + "" // , Pattern.DOTALL); - private static final Pattern P_CONNECTIONS_DETAILS_COARSE = Pattern.compile("Detailansicht
" // id - + "(.*?)" // - + "\nDauer:", Pattern.DOTALL); - private static final Pattern P_CONNECTION_DETAILS_COARSE = Pattern.compile("\n(.*?)\n\n" // - + "\n(.*?)\n\n\n(.*?)\n", Pattern.DOTALL); private static final Pattern P_CONNECTION_DETAILS_FINE = Pattern.compile(".*?" // - + "]*>\n" // - + "(?:]*>)?" // departureId + + "(?:]*>)?" // departureId + "([^\n<]*).*?" // departure - + "]*>\n(?:(\\d{2}\\.\\d{2}\\.\\d{2})| )\n.*?" // departureDate - + "]*>(?:(\\d{2}:\\d{2})| ).*?" // departureTime - + "]*>\\s*(?: |(.*?))\\s*.*?" // departurePosition - + "(?:(\\d{2}\\.\\d{2}\\.\\d{2})| ).*?" // departureDate + + "\n?ab (\\d{2}:\\d{2}).*?" // departureTime + + "\\s*(?: |(.*?))\\s*.*?" // departurePosition + + "]*>(.*?).*?)?" // line - + "]*>\n" // - + "(?:]*>)?" // arrivalId + + "(?:]*>)?" // arrivalId + "([^\n<]*).*?" // arrival - + "]*>\n(?:(\\d{2}\\.\\d{2}\\.\\d{2})| )\n.*?" // arrivalDate - + "]*>(?:(\\d{2}:\\d{2})| ).*?" // arrivalTime - + "]*>\\s*(?: |(.*?))\\s*.*?" // arrivalPosition - + "(?:ca\\. (\\d+) Min\\.\n.*?)?" // min + + "(?:(\\d{2}\\.\\d{2}\\.\\d{2})| ).*?" // arrivalDate + + "\n?an (\\d{2}:\\d{2}).*?" // arrivalTime + + "\\s*(?: |(.*?))\\s*.*?" // arrivalPosition , Pattern.DOTALL); private QueryConnectionsResult queryConnections(final String firstUri, final CharSequence firstPage) throws IOException { // ugly workaround to fetch all details - final Matcher mFormAction = P_CONNECTIONS_FORM_ACTION.matcher(firstPage); - if (!mFormAction.find()) - throw new IOException("cannot find form action in '" + firstPage + "' on " + firstUri); - final String baseUri = mFormAction.group(1); - final String query = "sortConnections=minDeparture&guiVCtrl_connection_detailsOut_add_group_overviewOut=yes"; - final CharSequence page = ParserUtils.scrape(baseUri, true, query, null, true); + final Matcher mAllDetailsAction = P_CONNECTIONS_ALL_DETAILS.matcher(firstPage); + if (!mAllDetailsAction.find()) + throw new IOException("cannot find all details link in '" + firstPage + "' on " + firstUri); + final String allDetailsUri = mAllDetailsAction.group(1); + final CharSequence page = ParserUtils.scrape(allDetailsUri, false, null, null, true); final Matcher mError = P_QUERY_CONNECTIONS_ERROR.matcher(page); if (mError.find()) @@ -314,60 +302,43 @@ public class OebbProvider implements NetworkProvider } // parse page - final Matcher mPage = P_CONNECTIONS_PAGE.matcher(page); - if (mPage.matches()) + final Matcher mHead = P_CONNECTIONS_HEAD.matcher(page); + if (mHead.matches()) { - final String action = mPage.group(1); - final String headSet = mPage.group(2) + mPage.group(4); + final String from = ParserUtils.resolveEntities(mHead.group(1)); + final String to = ParserUtils.resolveEntities(mHead.group(2)); + final Date currentDate = ParserUtils.parseDate(mHead.group(3)); + final String linkEarlier = mHead.group(4) != null ? ParserUtils.resolveEntities(mHead.group(4)) : null; + final String linkLater = mHead.group(5) != null ? ParserUtils.resolveEntities(mHead.group(5)) : null; + final List connections = new ArrayList(); - final Matcher mHead = P_CONNECTIONS_HEAD.matcher(headSet); - if (mHead.matches()) + Date lastDate = null; + + final Matcher mConCoarse = P_CONNECTIONS_COARSE.matcher(page); + while (mConCoarse.find()) { - final String from = ParserUtils.resolveEntities(mHead.group(1)); - final Date currentDate = ParserUtils.parseDate(mHead.group(2)); - final String to = ParserUtils.resolveEntities(mHead.group(3)); - final String linkEarlier = mHead.group(4) != null ? action + "&REQ0HafasScrollDir=2" + ParserUtils.resolveEntities(mHead.group(4)) - : null; - final String linkLater = mHead.group(5) != null ? action + "&REQ0HafasScrollDir=1" + ParserUtils.resolveEntities(mHead.group(5)) - : null; - final List connections = new ArrayList(); + final String id = mConCoarse.group(1); + final String overview = mConCoarse.group(2); + final String details = mConCoarse.group(3); - final Matcher mConCoarse = P_CONNECTIONS_COARSE.matcher(mPage.group(3)); - while (mConCoarse.find()) + final Matcher mConFine = P_CONNECTIONS_FINE.matcher(overview); + if (mConFine.matches()) { - final Matcher mConFine = P_CONNECTIONS_FINE.matcher(mConCoarse.group(1)); - if (mConFine.matches()) - { - final String id = mConFine.group(1); - final Date departureDate = ParserUtils.parseDate(mConFine.group(2)); - final Date arrivalDate = mConFine.group(3) != null ? ParserUtils.parseDate(mConFine.group(3)) : null; - final Date departureTime = ParserUtils.joinDateTime(departureDate, ParserUtils.parseTime(mConFine.group(4))); - final Date arrivalTime = ParserUtils.joinDateTime(arrivalDate != null ? arrivalDate : departureDate, ParserUtils - .parseTime(mConFine.group(5))); - final String link = firstUri + "#" + id; // TODO use print link? + final Date overviewDepartureDate = ParserUtils.parseDate(mConFine.group(1)); + final Date overviewArrivalDate = mConFine.group(2) != null ? ParserUtils.parseDate(mConFine.group(2)) : null; + final Date overviewDepartureTime = ParserUtils.joinDateTime(overviewDepartureDate, ParserUtils.parseTime(mConFine.group(3))); + final Date overviewArrivalTime = ParserUtils.joinDateTime(overviewArrivalDate != null ? overviewArrivalDate + : overviewDepartureDate, ParserUtils.parseTime(mConFine.group(4))); + final String link = allDetailsUri; // TODO use print link? - final Connection connection = new Connection(id, link, departureTime, arrivalTime, null, null, 0, from, 0, to, - new ArrayList(1)); - connections.add(connection); - } - else - { - throw new IllegalArgumentException("cannot parse '" + mConCoarse.group(1) + "' on " + baseUri + " (POST)"); - } - } + final Connection connection = new Connection(id, link, overviewDepartureTime, overviewArrivalTime, null, null, 0, from, 0, to, + new ArrayList(1)); + connections.add(connection); - final Matcher mConDetCoarse = P_CONNECTIONS_DETAILS_COARSE.matcher(mPage.group(5)); - while (mConDetCoarse.find()) - { - final String id = mConDetCoarse.group(1); - final Connection connection = findConnection(connections, id); - - Date lastDate = null; - - final Matcher mDetCoarse = P_CONNECTION_DETAILS_COARSE.matcher(mConDetCoarse.group(2)); + final Matcher mDetCoarse = P_CONNECTION_DETAILS_COARSE.matcher(details); while (mDetCoarse.find()) { - final String set = mDetCoarse.group(1) + mDetCoarse.group(2) + mDetCoarse.group(3); + final String set = mDetCoarse.group(1) + mDetCoarse.group(2); final Matcher mDetFine = P_CONNECTION_DETAILS_FINE.matcher(set); if (mDetFine.matches()) @@ -376,11 +347,14 @@ public class OebbProvider implements NetworkProvider final String departure = ParserUtils.resolveEntities(mDetFine.group(2)); - Date departureDate = mDetFine.group(3) != null ? ParserUtils.parseDate(mDetFine.group(3)) : null; - if (departureDate != null) - lastDate = departureDate; + Date detailsDepartureDate = mDetFine.group(3) != null ? ParserUtils.parseDate(mDetFine.group(3)) : null; + if (detailsDepartureDate != null) + lastDate = detailsDepartureDate; else - departureDate = lastDate; + detailsDepartureDate = lastDate; + + final Date detailsDepartureTime = ParserUtils.parseTime(mDetFine.group(4)); + final Date detailsDepartureDateTime = ParserUtils.joinDateTime(detailsDepartureDate, detailsDepartureTime); final String lineType = mDetFine.group(6); @@ -388,20 +362,20 @@ public class OebbProvider implements NetworkProvider final String arrival = ParserUtils.resolveEntities(mDetFine.group(9)); - Date arrivalDate = mDetFine.group(10) != null ? ParserUtils.parseDate(mDetFine.group(10)) : null; - if (arrivalDate != null) - lastDate = arrivalDate; + Date detailsArrivalDate = mDetFine.group(10) != null ? ParserUtils.parseDate(mDetFine.group(10)) : null; + if (detailsArrivalDate != null) + lastDate = detailsArrivalDate; else - arrivalDate = lastDate; + detailsArrivalDate = lastDate; + + final Date detailsArrivalTime = ParserUtils.parseTime(mDetFine.group(11)); + final Date detailsArrivalDateTime = ParserUtils.joinDateTime(detailsArrivalDate, detailsArrivalTime); if (!lineType.equals("fuss")) { if (departureId == 0) throw new IllegalStateException("departureId"); - final Date departureTime = ParserUtils.parseTime(mDetFine.group(4)); - final Date departureDateTime = ParserUtils.joinDateTime(departureDate, departureTime); - final String departurePosition = mDetFine.group(5) != null ? ParserUtils.resolveEntities(mDetFine.group(5)) : null; final String line = normalizeLine(lineType, ParserUtils.resolveEntities(mDetFine.group(7))); @@ -409,18 +383,15 @@ public class OebbProvider implements NetworkProvider if (arrivalId == 0) throw new IllegalStateException("arrivalId"); - final Date arrivalTime = ParserUtils.parseTime(mDetFine.group(11)); - final Date arrivalDateTime = ParserUtils.joinDateTime(arrivalDate, arrivalTime); - final String arrivalPosition = mDetFine.group(12) != null ? ParserUtils.resolveEntities(mDetFine.group(12)) : null; - final Connection.Trip trip = new Connection.Trip(line, LINES.get(line.charAt(0)), null, departureDateTime, - departurePosition, departureId, departure, arrivalDateTime, arrivalPosition, arrivalId, arrival); + final Connection.Trip trip = new Connection.Trip(line, LINES.get(line.charAt(0)), null, detailsDepartureDateTime, + departurePosition, departureId, departure, detailsArrivalDateTime, arrivalPosition, arrivalId, arrival); connection.parts.add(trip); } else { - final int min = Integer.parseInt(mDetFine.group(13)); + final int min = (int) (detailsArrivalDateTime.getTime() - detailsDepartureDateTime.getTime()) / 1000 / 60; final Connection.Footway footway = new Connection.Footway(min, departureId, departure, arrivalId, arrival); connection.parts.add(footway); @@ -428,33 +399,25 @@ public class OebbProvider implements NetworkProvider } else { - throw new IllegalArgumentException("cannot parse '" + set + "' on " + baseUri + " (POST)"); + throw new IllegalArgumentException("cannot parse '" + set + "' on " + allDetailsUri); } } } + else + { + throw new IllegalArgumentException("cannot parse '" + overview + "' on " + allDetailsUri); + } - return new QueryConnectionsResult(baseUri, from, to, currentDate, linkEarlier, linkLater, connections); - } - else - { - throw new IllegalArgumentException("cannot parse '" + headSet + "' on " + baseUri + " (POST)"); } + + return new QueryConnectionsResult(allDetailsUri, from, to, currentDate, linkEarlier, linkLater, connections); } else { - throw new IOException(page.toString()); + throw new IllegalArgumentException("cannot parse '" + page + "' on " + allDetailsUri); } } - private Connection findConnection(final List connections, final String id) - { - for (final Connection connection : connections) - if (connection.id.equals(id)) - return connection; - - return null; - } - public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException { throw new UnsupportedOperationException(); @@ -603,24 +566,24 @@ public class OebbProvider implements NetworkProvider return 'I'; if (ucType.equals("ICE")) // InterCityExpress return 'I'; - // if (ucType.equals("X")) // Interconnex + // if (ucType.equals("X")) // Interconnex, Connections only? // return 'I'; if (ucType.equals("EN")) // EuroNight return 'I'; if (ucType.equals("CNL")) // CityNightLine return 'I'; - // if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva - // return 'I'; - // if (ucType.equals("INT")) // Rußland - // return 'I'; + if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva, Connections only? + return 'I'; + if (ucType.equals("INT")) // Rußland, Connections only? + return 'I'; if (ucType.equals("D")) // Rußland return 'I'; - // if (ucType.equals("RR")) // Finnland - // return 'I'; + if (ucType.equals("RR")) // Finnland, Connections only? + return 'I'; if (ucType.equals("TLK")) // Tanie Linie Kolejowe, Polen return 'I'; - // if (ucType.equals("EE")) // Rumänien - // return 'I'; + if (ucType.equals("EE")) // Rumänien, Connections only? + return 'I'; if (ucType.equals("SC")) // SuperCity, Tschechien return 'I'; if (ucType.equals("RJ")) // RailJet, Österreichische Bundesbahnen @@ -637,14 +600,14 @@ public class OebbProvider implements NetworkProvider return 'I'; if (ucType.equals("AVE")) // Alta Velocidad Española, Spanien return 'I'; - // if (ucType.equals("INZ")) // Schweden, Nacht - // return 'I'; + if (ucType.equals("INZ")) // Schweden, Nacht, Connections only? + return 'I'; if (ucType.equals("NZ")) // Schweden, Nacht, via JSON API return 'I'; - // if (ucType.equals("OZ")) // Schweden, Oeresundzug - // return 'I'; - // if (ucType.equals("X2")) // Schweden - // return 'I'; + if (ucType.equals("OZ")) // Schweden, Oeresundzug, Connections only? + return 'I'; + if (ucType.equals("X2")) // Schweden, Connections only? + return 'I'; if (ucType.equals("X")) // Schweden, via JSON API return 'I'; if (ucType.equals("THA")) // Thalys @@ -659,37 +622,37 @@ public class OebbProvider implements NetworkProvider return 'I'; if (ucType.equals("ICN")) // Italien, Nacht return 'I'; - // if (ucType.equals("UUU")) // Italien, Nacht - // return 'I'; - // if (ucType.equals("RHI")) // ICE - // return 'I'; - // if (ucType.equals("RHT")) // TGV - // return 'I'; - // if (ucType.equals("TGD")) // TGV - // return 'I'; - // if (ucType.equals("ECB")) // EC - // return 'I'; - // if (ucType.equals("IRX")) // IC - // return 'I'; - // if (ucType.equals("AIR")) - // return 'I'; - // + if (ucType.equals("UUU")) // Italien, Nacht, Connections only? + return 'I'; + if (ucType.equals("RHI")) // ICE, Connections only? + return 'I'; + if (ucType.equals("RHT")) // TGV, Connections only? + return 'I'; + if (ucType.equals("TGD")) // TGV, Connections only? + return 'I'; + if (ucType.equals("ECB")) // EC, Connections only? + return 'I'; + if (ucType.equals("IRX")) // IC, Connections only? + return 'I'; + if (ucType.equals("AIR")) // Connections only? + return 'I'; + if (ucType.equals("R")) return 'R'; if (ucType.equals("REX")) // RegionalExpress return 'R'; - // if (ucType.equals("ZUG")) - // return 'R'; + if (ucType.equals("ZUG")) // Connections only? + return 'R'; if (ucType.equals("EZ")) // Erlebniszug return 'R'; - // if (ucType.equals("S2")) // Helsinki-Turku - // return 'R'; + if (ucType.equals("S2")) // Helsinki-Turku, Connections only? + return 'R'; if (ucType.equals("RB")) // RegionalBahn Deutschland return 'R'; if (ucType.equals("RE")) // RegionalExpress Deutschland return 'R'; - // if (ucType.equals("DPN")) // TODO nicht evtl. doch eher ne S-Bahn? - // return 'R'; + if (ucType.equals("DPN")) // Connections only? TODO nicht evtl. doch eher ne S-Bahn? + return 'R'; if (ucType.equals("BRB")) // ABELLIO Rail, via JSON API return 'R'; if (ucType.equals("ABR")) // Bayerische Regiobahn, via JSON API @@ -732,8 +695,8 @@ public class OebbProvider implements NetworkProvider return 'R'; if (ucType.equals("N")) // Frankreich, Tours return 'R'; - // if (ucType.equals("DPF")) // VX=Vogtland Express - // return 'R'; + if (ucType.equals("DPF")) // VX=Vogtland Express, Connections only? + return 'R'; if (ucType.equals("VBG")) // Vogtlandbahn, via JSON API return 'R'; if (ucType.equals("SBE")) // Zittau-Seifhennersdorf, via JSON API @@ -849,8 +812,8 @@ public class OebbProvider implements NetworkProvider if (ucType.equals("STR")) return 'T'; - // if (ucType.equals("LKB")) - // return 'T'; + if (ucType.equals("LKB")) // Connections only? + return 'T'; if (ucType.equals("WLB")) // via JSON API return 'T'; @@ -858,20 +821,20 @@ public class OebbProvider implements NetworkProvider return 'B'; if (ucType.equals("RFB")) return 'B'; - // if (ucType.equals("OBU")) - // return 'B'; + if (ucType.equals("OBU")) // Connections only? + return 'B'; if (ucType.equals("AST")) return 'B'; if (ucType.equals("ASTSV")) // via JSON API return 'B'; if (ucType.equals("ICB")) // ÖBB ICBus return 'B'; - // if (ucType.equals("FB")) // Polen - // return 'B'; - // if (ucType.equals("BSV")) // Deutschland - // return 'B'; - // if (ucType.equals("LT")) // Linien-Taxi - // return 'B'; + if (ucType.equals("FB")) // Polen, Connections only? + return 'B'; + if (ucType.equals("BSV")) // Deutschland, Connections only? + return 'B'; + if (ucType.equals("LT")) // Linien-Taxi, Connections only? + return 'B'; if (ucType.equals("BUSSV")) // via JSON API return 'B'; if (ucType.equals("BUSLEOBE")) // Rufbus, via JSON API @@ -883,15 +846,15 @@ public class OebbProvider implements NetworkProvider if (ucType.equals("O-B")) // Stadtbus, via JSON API return 'B'; - // if (ucType.equals("SCH")) - // return 'F'; + if (ucType.equals("SCH")) // Connections only? + return 'F'; if (ucType.equals("AS")) // SyltShuttle return 'F'; if (ucType.equals("SCHIFF")) // via JSON API return 'F'; - // if (ucType.equals("SB")) - // return 'C'; + if (ucType.equals("SB")) // Connections only? + return 'C'; if (ucType.equals("LIF")) return 'C'; if (ucType.equals("SEILBAHN")) // via JSON API @@ -900,14 +863,14 @@ public class OebbProvider implements NetworkProvider if (ucType.equals("FLUG")) // via JSON API return 'I'; - // if (ucType.equals("U70")) // U.K. - // return '?'; - // if (ucType.equals("R84")) - // return '?'; - // if (ucType.equals("S84")) - // return '?'; - // if (ucType.equals("T84")) - // return '?'; + if (ucType.equals("U70")) // U.K., Connections only? + return '?'; + if (ucType.equals("R84")) // U.K., Connections only? + return '?'; + if (ucType.equals("S84")) // U.K., Connections only? + return '?'; + if (ucType.equals("T84")) // U.K., Connections only? + return '?'; return 0; }