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
+ + "", 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" //
+ + " \n" //
+ + " | \n" //
+ + "\n" //
+ + "Aarau | \n" //
+ + "\n" //
+ + " | \n" //
+ + "ab | 15:47 | \n" //
+ + "5 \n" //
+ + " | \n" //
+ + "\n" //
+ + " \n" //
+ + "\n" //
+ + "IR 1928\n" //
+ + "\n" //
+ + " | \n" //
+ + "\n" //
+ + "\n" //
+ + " \n" //
+ + "1.  \n" //
+ + " \n" //
+ + " \n" //
+ + "2.  \n" //
+ + " \n" //
+ + " \n" //
+ + " | \n" //
+ + "\n" //
+ + "\n" //
+ + "\n" //
+ + " | \n" //
+ + "\n" //
+ + "Bern | \n" //
+ + "\n" //
+ + " | \n" //
+ + "an | 16:25 | \n" //
+ + "10 \n" //
+ + " | ");
+ }
+
+ @Test
+ public void footway()
+ {
+ assertFineConnectionDetails("\n" //
+ + "\n" //
+ + " \n" //
+ + " | \n" //
+ + "\n" //
+ + "Amriswil, Bahnhof | \n" //
+ + "\n" //
+ + " | \n" //
+ + " | | \n" //
+ + " | \n" //
+ + "\n" //
+ + " \n" //
+ + "Fussweg\n" //
+ + " | \n" //
+ + "\n" //
+ + "\n" //
+ + " \n" //
+ + " \n" //
+ + " \n" //
+ + " \n" //
+ + " \n" //
+ + " | \n" //
+ + "\n" //
+ + "\n" //
+ + "\n" //
+ + " | \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);
}
}