diff --git a/src/de/schildbach/pte/SbbProvider.java b/src/de/schildbach/pte/SbbProvider.java index 18efe6d3..2a246749 100644 --- a/src/de/schildbach/pte/SbbProvider.java +++ b/src/de/schildbach/pte/SbbProvider.java @@ -42,7 +42,7 @@ public class SbbProvider implements NetworkProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability != Capability.DEPARTURES) + if (capability == Capability.NEARBY_STATIONS) return false; return true; @@ -93,7 +93,12 @@ public class SbbProvider implements NetworkProvider final StringBuilder uri = new StringBuilder(); uri.append("http://fahrplan.sbb.ch/bin/query.exe/dn"); - uri.append("?REQ0HafasInitialSelection=0"); + uri.append("?OK"); + uri.append("&REQ0HafasMaxChangeTime=120"); + uri.append("&REQ0HafasOptimize1=").append(ParserUtils.urlEncode("1:1")); + uri.append("&REQ0HafasSearchForw=").append(dep ? "1" : "0"); + uri.append("&REQ0HafasSkipLongChanges=1"); + uri.append("&REQ0JourneyDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); uri.append("&REQ0JourneyStopsS0G=").append(ParserUtils.urlEncode(from)); uri.append("&REQ0JourneyStopsS0A=1"); uri.append("&REQ0JourneyStopsS0ID="); @@ -101,15 +106,14 @@ public class SbbProvider implements NetworkProvider { uri.append("&REQ0JourneyStops1.0G=").append(ParserUtils.urlEncode(via)); uri.append("&REQ0JourneyStops1.0A=1"); + uri.append("&REQ0JourneyStops1.0ID="); } uri.append("&REQ0JourneyStopsZ0G=").append(ParserUtils.urlEncode(to)); uri.append("&REQ0JourneyStopsZ0A=1"); uri.append("&REQ0JourneyStopsZ0ID="); - uri.append("&REQ0HafasSearchForw=").append(dep ? "1" : "0"); - uri.append("&REQ0JourneyDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); uri.append("&REQ0JourneyTime=").append(ParserUtils.urlEncode(TIME_FORMAT.format(date))); + uri.append("&queryPageDisplayed=yes"); uri.append("&start=Suchen"); - return uri.toString(); } @@ -177,12 +181,31 @@ public class SbbProvider implements NetworkProvider 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 + "ab.*?(\\d{2}:\\d{2}).*?" // departureTime + "duration.*?\\d{1,2}:\\d{2}.*?" // + "(?:.., (\\d{2}\\.\\d{2}\\.\\d{2}).*?)?" // arrivalDate + "an.*?(\\d{2}:\\d{2}).*?" // arrivalTime , Pattern.DOTALL); + private static final Pattern P_CONNECTIONS_DETAILS_COARSE = Pattern.compile(".*?" // id + + "\n?.*?(.*?)
", Pattern.DOTALL); + private static final Pattern P_CONNECTION_DETAILS_COARSE = Pattern.compile("(.*?class=\"stop-station-icon\".*?)\n?" // + + "(.*?class=\"stop-station-icon last\".*?)", Pattern.DOTALL); + static final Pattern P_CONNECTION_DETAILS_FINE = Pattern.compile(".*?" // + + "
" // departureId + + "(.*?).*?" // departure + + "\n?(?:.., (\\d{2}\\.\\d{2}\\.\\d{2})\n?)?.*?" // departureDate + + "(?:(\\d{2}:\\d{2})| ).*?" // departureTime + + "\n?\\s*(.+?)?\\s*\n?.*?" // departurePosition + + "\"(.*?)\".*?.*?" // line + + "(?:\n?(\\d+) Min\\..*?)?.*?" // min + + "" // arrivalId, + + "(.*?).*?" // arrival + + "\n?(?:.., (\\d{2}\\.\\d{2}\\.\\d{2})\n?)?.*?" // arrivalDate + + "(?:(\\d{2}:\\d{2})| ).*?" // arrivalTime + + "\n?\\s*(.+?)?\\s*\n?.*?" // arrivalPosition + , Pattern.DOTALL); private QueryConnectionsResult queryConnections(final String uri, final CharSequence page) throws IOException { @@ -197,23 +220,22 @@ public class SbbProvider implements NetworkProvider final List connections = new ArrayList(); final Matcher mConCoarse = P_CONNECTIONS_COARSE.matcher(page); - int i = 1; while (mConCoarse.find()) { final String set = mConCoarse.group(2) + mConCoarse.group(3); final Matcher mConFine = P_CONNECTIONS_FINE.matcher(set); if (mConFine.matches()) { - final Date departureDate = ParserUtils.parseDate(mConFine.group(1)); - final Date departureTime = ParserUtils.joinDateTime(departureDate, ParserUtils.parseTime(mConFine.group(2))); - final Date arrivalDate = mConFine.group(3) != null ? ParserUtils.parseDate(mConFine.group(3)) : null; + final String id = mConFine.group(1); + final Date departureDate = ParserUtils.parseDate(mConFine.group(2)); + final Date departureTime = ParserUtils.joinDateTime(departureDate, ParserUtils.parseTime(mConFine.group(3))); + final Date arrivalDate = mConFine.group(4) != null ? ParserUtils.parseDate(mConFine.group(4)) : null; final Date arrivalTime = ParserUtils.joinDateTime(arrivalDate != null ? arrivalDate : departureDate, ParserUtils - .parseTime(mConFine.group(4))); - final String id = departureTime.toString() + arrivalTime.toString(); + .parseTime(mConFine.group(5))); + final String link = uri + "#" + id; // TODO use print link? - final Connection connection = new Connection(id, uri + "#" + i++, departureTime, arrivalTime, 0, from, 0, to, + final Connection connection = new Connection(id, link, departureTime, arrivalTime, 0, from, 0, to, new ArrayList(1)); - connection.parts.add(new Connection.Trip(departureTime, arrivalTime, null, null)); connections.add(connection); } else @@ -222,6 +244,75 @@ public class SbbProvider implements NetworkProvider } } + final Matcher mConDetCoarse = P_CONNECTIONS_DETAILS_COARSE.matcher(page); + 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)); + while (mDetCoarse.find()) + { + final String set = mDetCoarse.group(1) + mDetCoarse.group(2); + + final Matcher mDetFine = P_CONNECTION_DETAILS_FINE.matcher(set); + if (mDetFine.matches()) + { + final int departureId = Integer.parseInt(mDetFine.group(1)); + + final String departure = ParserUtils.resolveEntities(mDetFine.group(2)); + + final String lineType = mDetFine.group(6); + + final int arrivalId = Integer.parseInt(mDetFine.group(9)); + + final String arrival = ParserUtils.resolveEntities(mDetFine.group(10)); + + if (!lineType.equals("fuss") && !lineType.equals("transfer")) + { + Date departureDate = mDetFine.group(3) != null ? ParserUtils.parseDate(mDetFine.group(3)) : null; + if (departureDate != null) + lastDate = departureDate; + else + departureDate = lastDate; + + final Date departureTime = ParserUtils.joinDateTime(departureDate, ParserUtils.parseTime(mDetFine.group(4))); + + final String departurePosition = mDetFine.group(5) != null ? ParserUtils.resolveEntities(mDetFine.group(5)) : null; + + final String line = normalizeLine(lineType, ParserUtils.resolveEntities(mDetFine.group(7))); + + Date arrivalDate = mDetFine.group(11) != null ? ParserUtils.parseDate(mDetFine.group(11)) : null; + if (arrivalDate != null) + lastDate = arrivalDate; + else + arrivalDate = lastDate; + + final Date arrivalTime = ParserUtils.joinDateTime(arrivalDate, ParserUtils.parseTime(mDetFine.group(12))); + + final String arrivalPosition = mDetFine.group(13) != null ? ParserUtils.resolveEntities(mDetFine.group(13)) : null; + + final Connection.Trip trip = new Connection.Trip(line, LINES.get(line.charAt(0)), null, departureTime, departurePosition, + departureId, departure, arrivalTime, arrivalPosition, arrivalId, arrival); + connection.parts.add(trip); + } + else + { + final int min = Integer.parseInt(mDetFine.group(8)); + + final Connection.Footway footway = new Connection.Footway(min, departure, arrival); + connection.parts.add(footway); + } + } + else + { + throw new IllegalArgumentException("cannot parse '" + set + "' on " + uri); + } + } + } + return new QueryConnectionsResult(uri, from, to, currentDate, linkEarlier, linkLater, connections); } else @@ -230,6 +321,15 @@ public class SbbProvider implements NetworkProvider } } + private Connection findConnection(List connections, 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(); @@ -328,6 +428,41 @@ public class SbbProvider implements NetworkProvider private static final Pattern P_NORMALIZE_LINE = Pattern.compile("([A-Za-zÄÖÜäöüß]+)[\\s-]*(.*)"); + private static String normalizeLine(final String type, final String line) + { + String strippedLine; + final Matcher m = P_NORMALIZE_LINE.matcher(line); + if (m.matches()) + strippedLine = m.group(1) + m.group(2); + else + strippedLine = line; + + if (type.equals("ice")) // InterCityExpress + return "I" + strippedLine; + if (type.equals("ic")) // InterCity + return "I" + strippedLine; + if (type.equals("icn")) // Intercity-Neigezug, Schweiz + return "I" + strippedLine; + if (type.equals("tha")) // Thalys + return "I" + strippedLine; + if (type.equals("r")) + return "R" + strippedLine; + if (type.equals("re")) + return "R" + strippedLine; + if (type.equals("ir")) + return "R" + strippedLine; + if (type.matches("s\\d*")) + return "S" + strippedLine; + if (type.equals("tra")) + return "T" + strippedLine; + if (type.equals("bus")) + return "B" + strippedLine; + if (type.equals("tro")) + return "B" + strippedLine; + + throw new IllegalStateException("cannot normalize type " + type + " line " + line); + } + private static String normalizeLine(final String line) { // TODO IN Torino-Napoli diff --git a/test/de/schildbach/pte/SbbProviderTest.java b/test/de/schildbach/pte/SbbProviderTest.java new file mode 100644 index 00000000..f14d98c4 --- /dev/null +++ b/test/de/schildbach/pte/SbbProviderTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.pte; + +import static junit.framework.Assert.assertTrue; + +import java.util.regex.Matcher; + +import org.junit.Test; + +/** + * @author Andreas Schildbach + */ +public class SbbProviderTest +{ + @Test + public void tripWithoutDate() + { + assertFineConnectionDetails("\n" // + + "\"Umgebungskarte:\n" // + + "\n" // + + "\n" // + + "Aarau\n" // + + "\n" // + + "\n" // + + "ab15:47\n" // + + "5 \n" // + + "\n" // + + "\n" // + + "\"IR
\n" // + + "\n" // + + "IR 1928\n" // + + "\n" // + + "\n" // + + "\n" // + + "
\n" // + + "
\n" // + + "1. \"Tiefe\n" // + + "
\n" // + + "
\n" // + + "2. \"Tiefe\n" // + + "
\n" // + + "
\n" // + + "\n" // + + "\n" // + + "InterRegio \n" // + + "\n" // + + "\n" // + + "\"Umgebungskarte:\n" // + + "\n" // + + "Bern\n" // + + "\n" // + + "\n" // + + "an16:25\n" // + + "10 \n" // + + ""); + } + + @Test + public void footway() + { + assertFineConnectionDetails("\n" // + + "\n" // + + "\"Umgebungskarte:\n" // + + "\n" // + + "\n" // + + "Amriswil, Bahnhof\n" // + + "\n" // + + "\n" // + + "  \n" // + + "\n" // + + "\n" // + + "\"Fussweg\"
\n" // + + "Fussweg\n" // + + "\n" // + + "\n" // + + "
\n" // + + "
\n" // + + "
\n" // + + "
\n" // + + "
\n" // + + "
\n" // + + "\n" // + + "\n" // + + "1 Min., Y \n" // + + "\n" // + + "\n" // + + "\"Umgebungskarte:\n" // + + "\n" // + + "Amriswil\n" // + + "\n" // + + "\n" // + + "  \n" // + + "\n"); + } + + private void assertFineConnectionDetails(String s) + { + Matcher m = SbbProvider.P_CONNECTION_DETAILS_FINE.matcher(s); + assertTrue(m.matches()); + // ParserUtils.printGroups(m); + } +} diff --git a/test/de/schildbach/pte/live/SbbProviderLiveTest.java b/test/de/schildbach/pte/live/SbbProviderLiveTest.java index d922f791..38e41e41 100644 --- a/test/de/schildbach/pte/live/SbbProviderLiveTest.java +++ b/test/de/schildbach/pte/live/SbbProviderLiveTest.java @@ -32,9 +32,20 @@ public class SbbProviderLiveTest private SbbProvider provider = new SbbProvider(); @Test - public void connection() throws Exception + public void fastConnection() throws Exception { final QueryConnectionsResult result = provider.queryConnections("Zürich!", null, "Bern", new Date(), true); System.out.println(result); + final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); + System.out.println(moreResult); + } + + @Test + public void slowConnection() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections("Schocherswil, Alte Post!", null, "Laconnex, Mollach", new Date(), true); + System.out.println(result); + final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); + System.out.println(moreResult); } }