parse SBB connection overview

git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@59 0924bc21-9374-b0fa-ee44-9ff1593b38f0
This commit is contained in:
andreas.schildbach 2010-08-10 10:37:37 +00:00
parent 5510ccbf6b
commit 6f1ecafcb2
8 changed files with 169 additions and 25 deletions

View file

@ -265,7 +265,8 @@ public final class BahnProvider implements NetworkProvider
line = normalizeLine(line); line = normalizeLine(line);
else else
line = null; line = null;
final Connection connection = new Connection(link, departureTime, arrivalTime, 0, from, 0, to, new ArrayList<Connection.Part>(1)); final Connection connection = new Connection(ParserUtils.extractId(link), link, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line.charAt(0)) : null)); connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line.charAt(0)) : null));
connections.add(connection); connections.add(connection);
} }
@ -393,8 +394,8 @@ public final class BahnProvider implements NetworkProvider
if (firstDepartureTime == null || lastArrivalTime == null) if (firstDepartureTime == null || lastArrivalTime == null)
throw new IllegalStateException("could not parse all parts of:\n" + page + "\n" + parts); throw new IllegalStateException("could not parse all parts of:\n" + page + "\n" + parts);
return new GetConnectionDetailsResult(new Date(), new Connection(uri, firstDepartureTime, lastArrivalTime, 0, firstDeparture, 0, return new GetConnectionDetailsResult(new Date(), new Connection(ParserUtils.extractId(uri), uri, firstDepartureTime, lastArrivalTime, 0,
lastArrival, parts)); firstDeparture, 0, lastArrival, parts));
} }
else else
{ {

View file

@ -36,10 +36,10 @@ public final class Connection implements Serializable
final public String to; final public String to;
final public List<Part> parts; final public List<Part> parts;
public Connection(final String link, final Date departureTime, final Date arrivalTime, final int fromId, final String from, final int toId, public Connection(final String id, final String link, final Date departureTime, final Date arrivalTime, final int fromId, final String from,
final String to, final List<Part> parts) final int toId, final String to, final List<Part> parts)
{ {
this.id = extractId(link); this.id = id;
this.link = link; this.link = link;
this.departureTime = departureTime; this.departureTime = departureTime;
this.fromId = fromId; this.fromId = fromId;
@ -50,11 +50,6 @@ public final class Connection implements Serializable
this.parts = parts; this.parts = parts;
} }
public static String extractId(String link)
{
return link.substring(link.length() - 10);
}
@Override @Override
public String toString() public String toString()
{ {

View file

@ -299,7 +299,7 @@ public class MvvProvider implements NetworkProvider
Date arrivalTime = ParserUtils.joinDateTime(date, ParserUtils.parseTime(mConFine.group(5))); Date arrivalTime = ParserUtils.joinDateTime(date, ParserUtils.parseTime(mConFine.group(5)));
if (departureTime.after(arrivalTime)) if (departureTime.after(arrivalTime))
arrivalTime = ParserUtils.addDays(arrivalTime, 1); arrivalTime = ParserUtils.addDays(arrivalTime, 1);
final Connection connection = new Connection(link, departureTime, arrivalTime, 0, from, 0, to, final Connection connection = new Connection(ParserUtils.extractId(link), link, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1)); new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Trip(departureTime, arrivalTime, null, null)); connection.parts.add(new Connection.Trip(departureTime, arrivalTime, null, null));
connections.add(connection); connections.add(connection);
@ -311,7 +311,7 @@ public class MvvProvider implements NetworkProvider
final Date departureTime = calendar.getTime(); final Date departureTime = calendar.getTime();
calendar.add(Calendar.MINUTE, min); calendar.add(Calendar.MINUTE, min);
final Date arrivalTime = calendar.getTime(); final Date arrivalTime = calendar.getTime();
final Connection connection = new Connection(link, departureTime, arrivalTime, 0, from, 0, to, final Connection connection = new Connection(ParserUtils.extractId(link), link, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1)); new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Footway(min, from, to)); connection.parts.add(new Connection.Footway(min, from, to));
connections.add(connection); connections.add(connection);
@ -448,8 +448,8 @@ public class MvvProvider implements NetworkProvider
lastArrivalTime = calendar.getTime(); lastArrivalTime = calendar.getTime();
} }
return new GetConnectionDetailsResult(new Date(), new Connection(uri, firstDepartureTime, lastArrivalTime, 0, firstDeparture, 0, return new GetConnectionDetailsResult(new Date(), new Connection(ParserUtils.extractId(uri), uri, firstDepartureTime, lastArrivalTime, 0,
lastArrival, parts)); firstDeparture, 0, lastArrival, parts));
} }
else else
{ {

View file

@ -201,4 +201,9 @@ public final class ParserUtils
throw new RuntimeException(x); throw new RuntimeException(x);
} }
} }
public static String extractId(final String link)
{
return link.substring(link.length() - 10);
}
} }

View file

@ -264,7 +264,8 @@ public class RmvProvider implements NetworkProvider
line = normalizeLine(line); line = normalizeLine(line);
else else
line = null; line = null;
final Connection connection = new Connection(link, departureTime, arrivalTime, 0, from, 0, to, new ArrayList<Connection.Part>(1)); final Connection connection = new Connection(ParserUtils.extractId(link), link, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line.charAt(0)) : null)); connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line.charAt(0)) : null));
connections.add(connection); connections.add(connection);
} }
@ -380,8 +381,8 @@ public class RmvProvider implements NetworkProvider
} }
} }
return new GetConnectionDetailsResult(currentDate, new Connection(uri, firstDepartureTime, lastArrivalTime, 0, firstDeparture, 0, return new GetConnectionDetailsResult(currentDate, new Connection(ParserUtils.extractId(uri), uri, firstDepartureTime, lastArrivalTime,
lastArrival, parts)); 0, firstDeparture, 0, lastArrival, parts));
} }
else else
{ {

View file

@ -113,20 +113,121 @@ public class SbbProvider implements NetworkProvider
return uri.toString(); return uri.toString();
} }
private static final Pattern P_PRE_ADDRESS = Pattern.compile(
"<select name=\"(REQ0JourneyStopsS0K|REQ0JourneyStopsZ0K|REQ0JourneyStops1\\.0K)\" accesskey=\"f\".*?>(.*?)</select>", Pattern.DOTALL);
private static final Pattern P_ADDRESSES = Pattern.compile("<option.*?>\\s*(.*?)\\s*</option>", Pattern.DOTALL);
public QueryConnectionsResult queryConnections(final String from, final String via, final String to, final Date date, final boolean dep) public QueryConnectionsResult queryConnections(final String from, final String via, final String to, final Date date, final boolean dep)
throws IOException throws IOException
{ {
throw new UnsupportedOperationException(); final String uri = connectionsQueryUri(from, via, to, date, dep);
final CharSequence page = ParserUtils.scrape(uri);
// TODO errors
List<String> fromAddresses = null;
List<String> viaAddresses = null;
List<String> toAddresses = null;
final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page);
while (mPreAddress.find())
{
final String type = mPreAddress.group(1);
final String options = mPreAddress.group(2);
final Matcher mAddresses = P_ADDRESSES.matcher(options);
final List<String> addresses = new ArrayList<String>();
while (mAddresses.find())
{
final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim();
if (!addresses.contains(address))
addresses.add(address);
} }
public QueryConnectionsResult queryMoreConnections(String uri) throws IOException if (type.equals("REQ0JourneyStopsS0K"))
{ fromAddresses = addresses;
throw new UnsupportedOperationException(); else if (type.equals("REQ0JourneyStopsZ0K"))
toAddresses = addresses;
else if (type.equals("REQ0JourneyStops1.0K"))
viaAddresses = addresses;
else
throw new IOException(type);
} }
if (fromAddresses != null || viaAddresses != null || toAddresses != null)
return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses);
else
return queryConnections(uri, page);
}
public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException
{
final CharSequence page = ParserUtils.scrape(uri);
return queryConnections(uri, page);
}
private static final Pattern P_CONNECTIONS_HEAD = Pattern.compile(".*?" //
+ "Von:.*?<td .*?>(.*?)</td>.*?" // from
+ "Datum:.*?<td .*?>.., (\\d{2}\\.\\d{2}\\.\\d{2})</td>.*?" // date
+ "Nach:.*?<td .*?>(.*?)</td>.*?" // to
+ "(?:<a href=\"(http://fahrplan.sbb.ch/bin/query.exe/dn\\?seqnr=\\d+&ident=[\\w\\.]+&REQ0HafasScrollDir=2)\".*?>.*?)?" // linkEarlier
+ "(?:<a href=\"(http://fahrplan.sbb.ch/bin/query.exe/dn\\?seqnr=\\d+&ident=[\\w\\.]+&REQ0HafasScrollDir=1)\".*?>.*?)?" // linkLater
, Pattern.DOTALL);
private static final Pattern P_CONNECTIONS_COARSE = Pattern.compile("<tr class=\"(zebra-row-\\d)\">(.*?)</tr>\n?"//
+ "<tr class=\"\\1\">(.+?)</tr>", Pattern.DOTALL);
private static final Pattern P_CONNECTIONS_FINE = Pattern.compile(".*?" //
+ ".., (\\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 QueryConnectionsResult queryConnections(final String uri, final CharSequence page) throws IOException private QueryConnectionsResult queryConnections(final String uri, final CharSequence page) throws IOException
{ {
throw new UnsupportedOperationException(); final Matcher mHead = P_CONNECTIONS_HEAD.matcher(page);
if (mHead.matches())
{
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 ? ParserUtils.resolveEntities(mHead.group(4)) : null;
final String linkLater = mHead.group(5) != null ? ParserUtils.resolveEntities(mHead.group(5)) : null;
final List<Connection> connections = new ArrayList<Connection>();
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 Date arrivalTime = ParserUtils.joinDateTime(arrivalDate != null ? arrivalDate : departureDate, ParserUtils
.parseTime(mConFine.group(4)));
final String id = departureTime.toString() + arrivalTime.toString();
final Connection connection = new Connection(id, uri + "#" + i++, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Trip(departureTime, arrivalTime, null, null));
connections.add(connection);
}
else
{
throw new IllegalArgumentException("cannot parse '" + set + "' on " + uri);
}
}
return new QueryConnectionsResult(uri, from, to, currentDate, linkEarlier, linkLater, connections);
}
else
{
throw new IOException(page.toString());
}
} }
public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException

View file

@ -271,7 +271,8 @@ public final class VbbProvider implements NetworkProvider
if (departureTime.after(arrivalTime)) if (departureTime.after(arrivalTime))
arrivalTime = ParserUtils.addDays(arrivalTime, 1); arrivalTime = ParserUtils.addDays(arrivalTime, 1);
final String line = normalizeLine(ParserUtils.resolveEntities(mConFine.group(4))); final String line = normalizeLine(ParserUtils.resolveEntities(mConFine.group(4)));
final Connection connection = new Connection(link, departureTime, arrivalTime, 0, from, 0, to, new ArrayList<Connection.Part>(1)); final Connection connection = new Connection(ParserUtils.extractId(link), link, departureTime, arrivalTime, 0, from, 0, to,
new ArrayList<Connection.Part>(1));
connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line) : null)); connection.parts.add(new Connection.Trip(departureTime, arrivalTime, line, line != null ? LINES.get(line) : null));
connections.add(connection); connections.add(connection);
} }
@ -410,8 +411,8 @@ public final class VbbProvider implements NetworkProvider
} }
if (firstDepartureTime != null && lastArrivalTime != null) if (firstDepartureTime != null && lastArrivalTime != null)
return new GetConnectionDetailsResult(currentDate, new Connection(uri, firstDepartureTime, lastArrivalTime, firstDepartureId, return new GetConnectionDetailsResult(currentDate, new Connection(ParserUtils.extractId(uri), uri, firstDepartureTime,
firstDeparture, lastArrivalId, lastArrival, parts)); lastArrivalTime, firstDepartureId, firstDeparture, lastArrivalId, lastArrival, parts));
else else
return new GetConnectionDetailsResult(currentDate, null); return new GetConnectionDetailsResult(currentDate, null);
} }

View file

@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.schildbach.pte.live;
import java.util.Date;
import org.junit.Test;
import de.schildbach.pte.QueryConnectionsResult;
import de.schildbach.pte.SbbProvider;
/**
* @author Andreas Schildbach
*/
public class SbbProviderLiveTest
{
private SbbProvider provider = new SbbProvider();
@Test
public void connection() throws Exception
{
final QueryConnectionsResult result = provider.queryConnections("Zürich!", null, "Bern", new Date(), true);
System.out.println(result);
}
}